lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb in ably-rest-0.9.3 vs lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb in ably-rest-1.0.0
- old
+ new
@@ -180,11 +180,11 @@
end
context 'with :query_time option' do
let(:options) { { query_time: true } }
- it 'queries the server for the time' do
+ it 'queries the server for the time (#RSA10k)' do
expect(client).to receive(:time).and_call_original
auth.request_token({}, options)
end
end
@@ -402,22 +402,22 @@
let!(:auth_url_request_stub) do
stub_request(auth_method, auth_url).to_return(:status => 500)
end
it 'raises ServerError' do
- expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::ServerError)
+ expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::AuthenticationFailed)
end
end
context 'XML' do
let!(:auth_url_request_stub) do
stub_request(auth_method, auth_url).
to_return(:status => 201, :body => '<xml></xml>', :headers => { 'Content-Type' => 'application/xml' })
end
it 'raises InvalidResponseBody' do
- expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::InvalidResponseBody)
+ expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::AuthenticationFailed, /Content Type.*not supported/)
end
end
end
end
@@ -610,28 +610,49 @@
it 'issues a new token every time (#RSA10a)' do
expect { auth.authorize }.to change { auth.current_token_details }
end
end
- context 'query_time: true' do
+ context 'query_time: true with authorize' do
let(:local_time) { @now - 60 }
let(:server_time) { @now }
before do
@now = Time.now
allow(Time).to receive(:now).and_return(local_time)
end
- it 'only queries the server time once and then works out the offset, query_time option is never persisted' do
+ it 'only queries the server time once and then works out the offset, query_time option is never persisted (#RSA10k)' do
expect(client).to receive(:time).once.and_return(server_time)
auth.authorize({}, query_time: true)
auth.authorize({})
expect(auth.auth_options).to_not have_key(:query_time)
end
end
+ context 'query_time: true ClientOption when instanced' do
+ let(:local_time) { @now - 60 }
+ let(:server_time) { @now }
+
+ let(:client_options) { default_options.merge(key: api_key, query_time: true) }
+
+ before do
+ @now = Time.now
+ allow(Time).to receive(:now).and_return(local_time)
+ end
+
+ it 'only queries the server time once and then works out the offset, query_time option is never persisted (#RSA10k)' do
+ expect(client).to receive(:time).once.and_return(server_time)
+
+ auth.authorize({})
+ auth.authorize({})
+ auth.authorize({})
+ expect(auth.auth_options).to_not have_key(:query_time)
+ end
+ end
+
context 'TokenParams argument' do
let(:default_token_params) { { ttl: 23 } }
before do
auth.authorize default_token_params
@@ -642,22 +663,30 @@
auth.authorize
expect(old_token).to_not eql(auth.current_token_details)
expect(auth.token_params[:ttl]).to eql(23)
end
- it 'updates defaults when present and all previous configured TokenParams are discarded' do
+ it 'updates defaults when present and all previous configured TokenParams are discarded (#RSA10g)' do
old_token = auth.current_token_details
auth.authorize({ client_id: 'bob' })
expect(old_token).to_not eql(auth.current_token_details)
- expect(auth.token_params[:ttl]).to_not eql(23)
+ expect(auth.token_params[:ttl]).to_not eq(23)
expect(auth.token_params[:client_id]).to eql('bob')
end
it 'updates Auth#token_params attribute with an immutable hash' do
auth.authorize({ client_id: 'bob' })
expect { auth.token_params['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
end
+
+ it 'uses TokenParams#timestamp for this request but obtains a new timestamp for subsequence requests (#RSA10g)' do
+ timestamp = Time.now.to_i
+ expect(auth).to receive(:create_token_request).with({ timestamp: Time.now.to_i }, {}).once.and_call_original
+ expect(auth).to receive(:create_token_request).with({}, {}).once.and_call_original
+ auth.authorize(timestamp: Time.now.to_i)
+ auth.authorize
+ end
end
context 'AuthOptions argument' do
let(:token_ttl) { 2 }
let(:auth_callback) { Proc.new do
@@ -678,21 +707,33 @@
auth.authorize(nil, nil)
expect(@old_token).to_not eql(auth.current_token_details)
expect(auth.options[:auth_callback]).to eql(auth_callback)
end
- it 'updates defaults when present and all previous configured AuthOptions are discarded' do
+ it 'updates defaults when present and all previous configured AuthOptions are discarded (#RSA10g)' do
auth.authorize(nil, auth_method: :post)
expect(@old_token).to_not eql(auth.current_token_details)
expect(auth.options[:auth_callback]).to be_nil
expect(auth.options[:auth_method]).to eql(:post)
end
it 'updates Auth#options attribute with an immutable hash' do
auth.authorize(nil, auth_callback: Proc.new { '1231232.12321:12321312' })
expect { auth.options['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
end
+
+ it 'uses AuthOptions#query_time for this request and will not query_time for subsequent requests (#RSA10g)' do
+ expect(client).to receive(:time).once.and_call_original
+ auth.authorize({}, query_time: true)
+ auth.authorize
+ end
+
+ it 'uses AuthOptions#query_time for this request and will query_time again if provided subsequently' do
+ expect(client).to receive(:time).twice.and_call_original
+ auth.authorize({}, query_time: true)
+ auth.authorize({}, query_time: true)
+ end
end
context 'with previous authorisation' do
before do
auth.authorize
@@ -766,11 +807,11 @@
old_token_defaults = Ably::Auth::TOKEN_DEFAULTS
stub_const 'Ably::Auth::TOKEN_DEFAULTS', old_token_defaults.merge(renew_token_buffer: 0)
@block_called = 0
end
- let(:token_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, token_params: { ttl: 3 })) }
+ let(:token_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, default_token_params: { ttl: 3 })) }
let(:client_options) {
default_options.merge(token: token_client.auth.request_token.token, auth_callback: Proc.new do
@block_called += 1
token_client.auth.create_token_request
end)
@@ -838,25 +879,26 @@
it 'uses the key name from the client' do
expect(subject['keyName']).to eql(key_name)
end
- it 'uses the default TTL' do
- expect(subject['ttl']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl) * 1000)
+ it 'specifies no TTL (#RSA5)' do
+ expect(subject['ttl']).to be_nil
end
context 'with a :ttl option below the Token expiry buffer that ensures tokens are renewed 15s before they expire as they are considered expired' do
- let(:ttl) { 1 }
+ let(:ttl) { 1 }
+ let(:token_params) { { ttl: ttl } }
it 'uses the Token expiry buffer default + 10s to allow for a token request in flight' do
- expect(subject.ttl).to be > 1
- expect(subject.ttl).to be > Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER
+ expect(subject['ttl']).to be > 1
+ expect(subject['ttl']).to be > Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER
end
end
- it 'uses the default capability' do
- expect(subject['capability']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability).to_json)
+ it 'specifies no capability (#RSA6)' do
+ expect(subject['capability']).to be_nil
end
context 'the nonce' do
it 'is unique for every request' do
unique_nonces = 100.times.map { auth.create_token_request['nonce'] }
@@ -1046,10 +1088,46 @@
end
it 'cannot be renewed automatically' do
expect(token_auth_client.auth).to_not be_token_renewable
end
+
+ context 'and the token expires' do
+ let(:ttl) { 1 }
+
+ before do
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0 # allow token to be used even if about to expire
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0) # Ensure tokens issued expire immediately after issue
+
+ @token = auth.request_token(ttl: ttl)
+ WebMock.enable!
+ WebMock.disable_net_connect!
+
+ token_expired = {
+ "error" => {
+ "statusCode" => 401,
+ "code" => 40140,
+ "message" => "Token expired"
+ }
+ }
+
+ stub_request(:post, "https://#{environment}-rest.ably.io/channels/foo/publish").
+ to_return(status: 401, body: token_expired.to_json, headers: { 'Content-Type' => 'application/json' })
+ end
+
+ after do
+ WebMock.allow_net_connect!
+ WebMock.disable!
+ end
+
+ let(:token) { @token.token }
+
+ it 'should indicate an error and not retry the request (#RSA4a)' do
+ sleep ttl + 1
+ expect { token_auth_client.channels.get('foo').publish 'event' }.to raise_error(Ably::Exceptions::TokenExpired)
+ end
+ end
end
context 'when implicit as a result of using :client_id' do
let(:client_id) { '999' }
let(:client) do
@@ -1088,27 +1166,75 @@
it 'when a message is published' do
expect(client.channel('foo').publish('event', 'data')).to be_truthy
end
- it 'with capability and TTL defaults' do
+ it 'with capability and TTL defaults (#TK2a, #TK2b)' do
client.channel('foo').publish('event', 'data')
expect(token).to be_a(Ably::Models::TokenDetails)
- capability_with_str_key = Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)
+ capability_with_str_key = { "*" => ["*"] } # Ably default is all capabilities
capability = Hash[capability_with_str_key.keys.map(&:to_s).zip(capability_with_str_key.values)]
expect(token.capability).to eq(capability)
- expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
+ expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + 60 * 60) # Ably default is 1hr
expect(token.client_id).to eq(client_id)
end
specify '#client_id contains the client_id' do
expect(client.auth.client_id).to eql(client_id)
end
end
end
+ context 'when token expires' do
+ before do
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0 # allow token to be used even if about to expire
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0) # Ensure tokens issued expire immediately after issue
+ end
+
+ after do
+ WebMock.allow_net_connect!
+ WebMock.disable!
+ end
+
+ let(:client_options) { default_options.merge(use_token_auth: true, key: api_key, query_time: true, default_token_params: { ttl: 2 }) }
+ let(:channel) { client.channels.get(random_str) }
+ let(:token_expired_response) do
+ {
+ "error" => {
+ "statusCode" => 401,
+ "code" => 40140,
+ "message" => "Token expired"
+ }
+ }
+ end
+
+ it 'automatically renews the token (#RSA4b)' do
+ expect(auth.current_token_details).to be_nil
+ channel.publish 'event'
+ token = auth.current_token_details
+ expect(token).to_not be_nil
+ sleep 2.5
+ channel.publish 'event'
+ expect(auth.current_token_details).to_not eql(token)
+ end
+
+ it 'fails if the token renewal fails (#RSA4b)' do
+ expect(auth.current_token_details).to be_nil
+ channel.publish 'event'
+ token = auth.current_token_details
+ expect(token).to_not be_nil
+ sleep 2.5
+ WebMock.enable!
+ WebMock.disable_net_connect!
+ stub_request(:post, "https://#{environment}-rest.ably.io/keys/#{TestApp.instance.key_name}/requestToken").
+ to_return(status: 401, body: token_expired_response.to_json, headers: { 'Content-Type' => 'application/json' })
+ expect { channel.publish 'event' }.to raise_error Ably::Exceptions::TokenExpired
+ expect(auth.current_token_details).to eql(token)
+ end
+ end
+
context 'when :client_id is provided in a token' do
let(:client_id) { '123' }
let(:token) do
Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol).auth.request_token(client_id: client_id)
end
@@ -1197,19 +1323,22 @@
expect(auth).to be_using_basic_auth
end
end
context 'deprecated #authorise' do
- let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object) }
+ let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object, use_token_auth: true) }
let(:custom_logger) do
Class.new do
def initialize
@messages = []
end
[:fatal, :error, :warn, :info, :debug].each do |severity|
- define_method severity do |message|
- @messages << [severity, message]
+ define_method severity do |message, &block|
+ message_val = [message]
+ message_val << block.call if block
+
+ @messages << [severity, message_val.compact.join(' ')]
end
end
def logs
@messages