spec/acceptance/rest/auth_spec.rb in ably-0.8.15 vs spec/acceptance/rest/auth_spec.rb in ably-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
@@ -443,12 +443,12 @@
it 'uses the token request returned from the callback when requesting a new token' do
expect(request_token.client_id).to eql(client_id)
end
- context 'when authorised' do
- before { auth.authorise(token_params, auth_callback: auth_callback) }
+ context 'when authorized' do
+ before { auth.authorize(token_params, auth_callback: auth_callback) }
it "sets Auth#client_id to the new token's client_id" do
expect(auth.client_id).to eql(client_id)
end
@@ -498,12 +498,12 @@
expect(token_details.expires).to be_within(1).of(expires)
expect(token_details.issued).to be_within(1).of(issued)
expect(token_details.capability).to eql(capability)
end
- context 'when authorised' do
- before { auth.authorise(token_params, auth_callback: auth_callback) }
+ context 'when authorized' do
+ before { auth.authorize(token_params, auth_callback: auth_callback) }
it "sets Auth#client_id to the new token's client_id" do
expect(auth.client_id).to eql(client_id)
end
@@ -581,83 +581,112 @@
expect(token_details.client_id).to eql(client_id)
end
end
end
- context 'before #authorise has been called' do
+ context 'before #authorize has been called' do
it 'has no current_token_details' do
expect(auth.current_token_details).to be_nil
end
end
- describe '#authorise' do
+ describe '#authorize (#RSA10, #RSA10j)' do
context 'when called for the first time since the client has been instantiated' do
let(:auth_options) do
{ auth_url: 'http://somewhere.com/' }
end
let(:token_params) do
{ ttl: 55 }
end
it 'passes all auth_options and token_params to #request_token' do
expect(auth).to receive(:request_token).with(token_params, auth_options)
- auth.authorise token_params, auth_options
+ auth.authorize token_params, auth_options
end
it 'returns a valid token' do
- expect(auth.authorise).to be_a(Ably::Models::TokenDetails)
+ expect(auth.authorize).to be_a(Ably::Models::TokenDetails)
end
- it 'issues a new token if option :force => true' do
- expect { auth.authorise(force: true) }.to change { auth.current_token_details }
+ 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.authorise({}, query_time: true)
- auth.authorise({}, force: true)
+ 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.authorise default_token_params
+ auth.authorize default_token_params
end
it 'has no effect on the defaults when null and TokenParam defaults remain the same' do
old_token = auth.current_token_details
- auth.authorise(nil, force: true)
+ 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.authorise({ client_id: 'bob' }, { force: true })
+ 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.authorise({ client_id: 'bob' }, { force: true })
+ 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
@@ -667,72 +696,79 @@
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
- auth.authorise(nil, default_auth_options)
+ auth.authorize(nil, default_auth_options)
@old_token = auth.current_token_details
sleep token_ttl + 0.5
end
it 'has no effect on the defaults when null and AuthOptions defaults remain the same' do
- auth.authorise(nil, nil)
+ 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
- auth.authorise(nil, auth_method: :post)
+ 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.authorise(nil, auth_callback: Proc.new { '1231232.12321:12321312' })
+ 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.authorise
+ auth.authorize
expect(auth.current_token_details).to_not be_expired
end
- it 'does not request a token if current_token_details has not expired' do
- expect(auth).to_not receive(:request_token)
- auth.authorise
- end
-
it 'requests a new token if token is expired' do
allow(auth.current_token_details).to receive(:expired?).and_return(true)
expect(auth).to receive(:request_token)
- expect { auth.authorise }.to change { auth.current_token_details }
+ expect { auth.authorize }.to change { auth.current_token_details }
end
- it 'issues a new token if option :force => true' do
- expect { auth.authorise({}, force: true) }.to change { auth.current_token_details }
+ it 'issues a new token every time #authorize is called' do
+ expect { auth.authorize({}) }.to change { auth.current_token_details }
end
end
- it 'updates the persisted token params that are then used for subsequent authorise requests' do
+ it 'updates the persisted token params that are then used for subsequent authorize requests' do
expect(auth.token_params[:ttl]).to_not eql(26)
- auth.authorise(ttl: 26)
+ auth.authorize(ttl: 26)
expect(auth.token_params[:ttl]).to eql(26)
end
- it 'updates the persisted auth options that are then used for subsequent authorise requests' do
+ it 'updates the persisted auth options that are then used for subsequent authorize requests' do
expect(auth.options[:authUrl]).to be_nil
- auth.authorise({}, authUrl: 'http://foo.com')
+ auth.authorize({}, authUrl: 'http://foo.com')
expect(auth.options[:authUrl]).to eql('http://foo.com')
end
context 'with a Proc for the :auth_callback option' do
let(:client_id) { random_str }
let!(:token) do
- auth.authorise({}, auth_callback: Proc.new do
+ auth.authorize({}, auth_callback: Proc.new do
@block_called ||= 0
@block_called += 1
auth.create_token_request(client_id: client_id)
end)
end
@@ -771,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)
@@ -798,27 +834,27 @@
context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
let(:auth_token_object) { auth_client.auth.request_token }
it 'rejects a TokenDetails object with an incompatible client_id and raises an exception' do
- expect { client.auth.authorise({}, force: true) }.to raise_error Ably::Exceptions::IncompatibleClientId
+ expect { client.auth.authorize({}) }.to raise_error Ably::Exceptions::IncompatibleClientId
end
end
context 'and an incompatible client_id in a TokenRequest object passed to the auth callback and raises an exception' do
let(:auth_token_object) { auth_client.auth.create_token_request }
it 'rejects a TokenRequests object with an incompatible client_id and raises an exception' do
- expect { client.auth.authorise({}, force: true) }.to raise_error Ably::Exceptions::IncompatibleClientId
+ expect { client.auth.authorize({}) }.to raise_error Ably::Exceptions::IncompatibleClientId
end
end
context 'and a token string without any retrievable client_id' do
let(:auth_token_object) { auth_client.auth.request_token(client_id: 'different').token }
it 'rejects a TokenRequests object with an incompatible client_id and raises an exception' do
- client.auth.authorise({}, force: true)
+ client.auth.authorize({})
expect(client.client_id).to eql(client_id)
end
end
end
end
@@ -836,32 +872,33 @@
it 'returns a TokenRequest that can be passed to a client that can use it for authentication without an API key' do
auth_callback = Proc.new { subject }
client_without_api_key = Ably::Rest::Client.new(default_options.merge(auth_callback: auth_callback))
expect(client_without_api_key.auth).to be_using_token_auth
- expect { client_without_api_key.auth.authorise }.to_not raise_error
+ expect { client_without_api_key.auth.authorize }.to_not raise_error
end
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'] }
@@ -895,11 +932,11 @@
end
it 'uses these capabilities when Ably issues an actual token' do
auth_callback = Proc.new { subject }
client_without_api_key = Ably::Rest::Client.new(default_options.merge(auth_callback: auth_callback))
- client_without_api_key.auth.authorise
+ client_without_api_key.auth.authorize
expect(client_without_api_key.auth.current_token_details.capability).to eql(capability)
end
end
context 'with additional invalid attributes' do
@@ -1006,11 +1043,11 @@
it 'is valid when used for authentication' do
auth_callback = Proc.new do
auth.create_token_request(token_attributes)
end
client = Ably::Rest::Client.new(auth_callback: auth_callback, environment: environment, protocol: protocol)
- client.auth.authorise
+ client.auth.authorize
end
end
end
end
@@ -1051,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
@@ -1093,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
@@ -1198,9 +1319,51 @@
expect(auth.key).to eql(api_key)
end
specify '#using_basic_auth? is true' do
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, 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, &block|
+ message_val = [message]
+ message_val << block.call if block
+
+ @messages << [severity, message_val.compact.join(' ')]
+ end
+ end
+
+ def logs
+ @messages
+ end
+
+ def level
+ 1
+ end
+
+ def level=(new_level)
+ end
+ end
+ end
+ let(:custom_logger_object) { custom_logger.new }
+
+ it 'logs a deprecation warning (#RSA10l)' do
+ client.auth.authorise
+ expect(custom_logger_object.logs.find { |severity, message| message.match(/authorise.*deprecated/i)} ).to_not be_nil
+ end
+
+ it 'returns a valid token (#RSA10l)' do
+ response = client.auth.authorise
+ expect(response).to be_a(Ably::Models::TokenDetails)
end
end
end
end