spec/acceptance/rest/auth_spec.rb in ably-0.6.2 vs spec/acceptance/rest/auth_spec.rb in ably-0.7.0
- old
+ new
@@ -1,498 +1,559 @@
+# encoding: utf-8
require 'spec_helper'
-require 'securerandom'
describe Ably::Auth do
include Ably::Modules::Conversions
- [:msgpack, :json].each do |protocol|
- context "over #{protocol}" do
- let(:client) do
- Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
+ def hmac_for(token_request, secret)
+ text = token_request.values_at(
+ :id,
+ :ttl,
+ :capability,
+ :client_id,
+ :timestamp,
+ :nonce
+ ).map { |t| "#{t}\n" }.join("")
+
+ encode64(
+ Digest::HMAC.digest(text, key_secret, Digest::SHA256)
+ )
+ end
+
+ vary_by_protocol do
+ let(:client) do
+ Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
+ end
+ let(:auth) { client.auth }
+ let(:content_type) do
+ if protocol == :msgpack
+ 'application/x-msgpack'
+ else
+ 'application/json'
end
- let(:auth) { client.auth }
- let(:content_type) do
- if protocol == :msgpack
- 'application/x-msgpack'
- else
- 'application/json'
- end
- end
+ end
- def request_body_includes(request, protocol, key, val)
- body = if protocol == :msgpack
- MessagePack.unpack(request.body)
- else
- JSON.parse(request.body)
- end
- body[key.to_s].to_s == val.to_s
+ def request_body_includes(request, protocol, key, val)
+ body = if protocol == :msgpack
+ MessagePack.unpack(request.body)
+ else
+ JSON.parse(request.body)
end
+ body[key.to_s].to_s == val.to_s
+ end
- def serialize(object, protocol)
- if protocol == :msgpack
- MessagePack.pack(token_response)
- else
- JSON.dump(token_response)
- end
+ def serialize(object, protocol)
+ if protocol == :msgpack
+ MessagePack.pack(token_response)
+ else
+ JSON.dump(token_response)
end
+ end
- describe "#request_token" do
- let(:ttl) { 30 * 60 }
- let(:capability) { { :foo => ['publish'] } }
+ it 'has immutable options' do
+ expect { auth.options['key_id'] = 'new_id' }.to raise_error RuntimeError, /can't modify frozen Hash/
+ end
- it 'returns the requested token' do
- actual_token = auth.request_token(
- ttl: ttl,
- capability: capability
- )
+ describe '#request_token' do
+ let(:ttl) { 30 * 60 }
+ let(:capability) { { :foo => ['publish'] } }
- expect(actual_token.id).to match(/^#{app_id}\.[\w-]+$/)
- expect(actual_token.key_id).to match(/^#{key_id}$/)
- expect(actual_token.issued_at).to be_within(2).of(Time.now)
- expect(actual_token.expires_at).to be_within(2).of(Time.now + ttl)
- end
+ it 'returns the requested token' do
+ actual_token = auth.request_token(
+ ttl: ttl,
+ capability: capability
+ )
- %w(client_id capability nonce timestamp ttl).each do |option|
- context "option :#{option}", webmock: true do
- let(:random) { SecureRandom.random_number(1_000_000_000).to_s }
- let(:options) { { option.to_sym => random } }
+ expect(actual_token.id).to match(/^#{app_id}\.[\w-]+$/)
+ expect(actual_token.key_id).to match(/^#{key_id}$/)
+ expect(actual_token.issued_at).to be_within(2).of(Time.now)
+ expect(actual_token.expires_at).to be_within(2).of(Time.now + ttl)
+ end
- let(:token_response) { { access_token: {} } }
- let!(:request_token_stub) do
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
- with do |request|
- request_body_includes(request, protocol, option, random)
- end.to_return(
- :status => 201,
- :body => serialize(token_response, protocol),
- :headers => { 'Content-Type' => content_type }
- )
- end
+ %w(client_id capability nonce timestamp ttl).each do |option|
+ context "option :#{option}", :webmock do
+ let(:random) { random_int_str }
+ let(:options) { { option.to_sym => random } }
- before { auth.request_token options }
-
- it 'overrides default' do
- expect(request_token_stub).to have_been_requested
- end
- end
- end
-
- context 'with :key_id & :key_secret options', webmock: true do
- let(:key_id) { SecureRandom.hex }
- let(:key_secret) { SecureRandom.hex }
- let(:nonce) { SecureRandom.hex }
- let(:token_options) { { key_id: key_id, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
- let(:token_request) { auth.create_token_request(token_options) }
- let(:mac) do
- hmac_for(token_request, key_secret)
- end
-
let(:token_response) { { access_token: {} } }
let!(:request_token_stub) do
stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
with do |request|
- request_body_includes(request, protocol, 'mac', mac)
+ request_body_includes(request, protocol, option, random)
end.to_return(
:status => 201,
:body => serialize(token_response, protocol),
- :headers => { 'Content-Type' => content_type })
+ :headers => { 'Content-Type' => content_type }
+ )
end
- let!(:token) { auth.request_token(token_options) }
+ before { auth.request_token options }
- specify 'key_id is used in request and signing uses key_secret' do
+ it 'overrides default' do
expect(request_token_stub).to have_been_requested
end
end
+ end
- context 'with :query_time option' do
- let(:options) { { query_time: true } }
+ context 'with :key_id & :key_secret options', :webmock do
+ let(:key_id) { random_str }
+ let(:key_secret) { random_str }
+ let(:nonce) { random_str }
+ let(:token_options) { { key_id: key_id, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
+ let(:token_request) { auth.create_token_request(token_options) }
+ let(:mac) do
+ hmac_for(token_request, key_secret)
+ end
- it 'queries the server for the time' do
- expect(client).to receive(:time).and_call_original
- auth.request_token(options)
- end
+ let(:token_response) { { access_token: {} } }
+ let!(:request_token_stub) do
+ stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
+ with do |request|
+ request_body_includes(request, protocol, 'mac', mac)
+ end.to_return(
+ :status => 201,
+ :body => serialize(token_response, protocol),
+ :headers => { 'Content-Type' => content_type })
end
- context 'without :query_time option' do
- let(:options) { { query_time: false } }
+ let!(:token) { auth.request_token(token_options) }
- it 'queries the server for the time' do
- expect(client).to_not receive(:time)
- auth.request_token(options)
- end
+ specify 'key_id is used in request and signing uses key_secret' do
+ expect(request_token_stub).to have_been_requested
end
+ end
- context 'with :auth_url option', webmock: true do
- let(:auth_url) { 'https://www.fictitious.com/get_token' }
- let(:token_request) { { id: key_id } }
- let(:token_response) { { access_token: { } } }
- let(:query_params) { nil }
- let(:headers) { nil }
- let(:auth_method) { :get }
- let(:options) do
- {
- auth_url: auth_url,
- auth_params: query_params,
- auth_headers: headers,
- auth_method: auth_method
- }
- end
+ context 'with :query_time option' do
+ let(:options) { { query_time: true } }
- let!(:auth_url_request_stub) do
- stub = stub_request(auth_method, auth_url)
- stub.with(:query => query_params) unless query_params.nil?
- stub.with(:headers => headers) unless headers.nil?
- stub.to_return(
+ it 'queries the server for the time' do
+ expect(client).to receive(:time).and_call_original
+ auth.request_token(options)
+ end
+ end
+
+ context 'without :query_time option' do
+ let(:options) { { query_time: false } }
+
+ it 'queries the server for the time' do
+ expect(client).to_not receive(:time)
+ auth.request_token(options)
+ end
+ end
+
+ context 'with :auth_url option', :webmock do
+ let(:auth_url) { 'https://www.fictitious.com/get_token' }
+ let(:token_request) { { id: key_id } }
+ let(:token_response) { { access_token: { } } }
+ let(:query_params) { nil }
+ let(:headers) { nil }
+ let(:auth_method) { :get }
+ let(:options) do
+ {
+ auth_url: auth_url,
+ auth_params: query_params,
+ auth_headers: headers,
+ auth_method: auth_method
+ }
+ end
+
+ let!(:auth_url_request_stub) do
+ stub = stub_request(auth_method, auth_url)
+ stub.with(:query => query_params) unless query_params.nil?
+ stub.with(:headers => headers) unless headers.nil?
+ stub.to_return(
+ :status => 201,
+ :body => token_request.to_json,
+ :headers => { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ let!(:request_token_stub) do
+ stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
+ with do |request|
+ request_body_includes(request, protocol, 'id', key_id)
+ end.to_return(
:status => 201,
- :body => token_request.to_json,
- :headers => { 'Content-Type' => 'application/json' }
+ :body => serialize(token_response, protocol),
+ :headers => { 'Content-Type' => content_type }
)
- end
+ end
- let!(:request_token_stub) do
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
- with do |request|
- request_body_includes(request, protocol, 'id', key_id)
- end.to_return(
- :status => 201,
- :body => serialize(token_response, protocol),
- :headers => { 'Content-Type' => content_type }
- )
+ context 'when response is valid' do
+ before { auth.request_token options }
+
+ it 'requests a token from :auth_url using an HTTP GET request' do
+ expect(request_token_stub).to have_been_requested
+ expect(auth_url_request_stub).to have_been_requested
end
- context 'valid' do
- before { auth.request_token options }
+ context 'with :query_params' do
+ let(:query_params) { { 'key' => random_str } }
- context 'and default options' do
- it 'requests a token from :auth_url' do
- expect(request_token_stub).to have_been_requested
- expect(auth_url_request_stub).to have_been_requested
- end
+ it 'requests a token from :auth_url with the :query_params' do
+ expect(request_token_stub).to have_been_requested
+ expect(auth_url_request_stub).to have_been_requested
end
+ end
- context 'with params' do
- let(:query_params) { { 'key' => SecureRandom.hex } }
- it 'requests a token from :auth_url' do
- expect(request_token_stub).to have_been_requested
- expect(auth_url_request_stub).to have_been_requested
- end
+ context 'with :headers' do
+ let(:headers) { { 'key' => random_str } }
+ it 'requests a token from :auth_url with the HTTP headers set' do
+ expect(request_token_stub).to have_been_requested
+ expect(auth_url_request_stub).to have_been_requested
end
+ end
- context 'with headers' do
- let(:headers) { { 'key' => SecureRandom.hex } }
- it 'requests a token from :auth_url' do
- expect(request_token_stub).to have_been_requested
- expect(auth_url_request_stub).to have_been_requested
- end
+ context 'with POST' do
+ let(:auth_method) { :post }
+ it 'requests a token from :auth_url using an HTTP POST instead of the default GET' do
+ expect(request_token_stub).to have_been_requested
+ expect(auth_url_request_stub).to have_been_requested
end
+ end
+ end
- context 'with POST' do
- let(:auth_method) { :post }
- it 'requests a token from :auth_url' do
- expect(request_token_stub).to have_been_requested
- expect(auth_url_request_stub).to have_been_requested
- end
+ context 'when response is invalid' do
+ context '500' do
+ 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 options }.to raise_error(Ably::Exceptions::ServerError)
+ end
end
- context 'when response is invalid' do
- context '500' do
- 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 options }.to raise_error(Ably::Exceptions::ServerError)
- 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
- 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 options }.to raise_error(Ably::Exceptions::InvalidResponseBody)
- end
+ it 'raises InvalidResponseBody' do
+ expect { auth.request_token options }.to raise_error(Ably::Exceptions::InvalidResponseBody)
end
end
end
+ end
- context 'with auth_block' do
- let(:client_id) { SecureRandom.hex }
- let(:options) { { client_id: client_id } }
- let!(:token) do
- auth.request_token(options) do |block_options|
- @block_called = true
- @block_options = block_options
- auth.create_token_request(client_id: client_id)
- end
+ context 'with token_request_block' do
+ let(:client_id) { random_str }
+ let(:options) { { client_id: client_id } }
+ let!(:token) do
+ auth.request_token(options) do |block_options|
+ @block_called = true
+ @block_options = block_options
+ auth.create_token_request(client_id: client_id)
end
+ end
- it 'calls the block' do
- expect(@block_called).to eql(true)
- expect(@block_options).to include(options)
- end
+ it 'calls the block when authenticating to obtain the request token' do
+ expect(@block_called).to eql(true)
+ expect(@block_options).to include(options)
+ end
- it 'uses the token request when requesting a new token' do
- expect(token.client_id).to eql(client_id)
- end
+ it 'uses the token request from the block when requesting a new token' do
+ expect(token.client_id).to eql(client_id)
end
end
+ end
- describe '#authorise' do
- context 'with no previous authorisation' do
- let(:request_options) do
- { auth_url: 'http://somewhere.com/' }
- end
+ context 'before #authorise has been called' do
+ it 'has no current_token' do
+ expect(auth.current_token).to be_nil
+ end
+ end
- it 'has no current_token' do
- expect(auth.current_token).to be_nil
- end
+ describe '#authorise' do
+ context 'when called for the first time since the client has been instantiated' do
+ let(:request_options) do
+ { auth_url: 'http://somewhere.com/' }
+ end
- it 'passes all options to request_token' do
- expect(auth).to receive(:request_token).with(request_options)
- auth.authorise request_options
- end
+ it 'passes all options to #request_token' do
+ expect(auth).to receive(:request_token).with(request_options)
+ auth.authorise request_options
+ end
- it 'returns a valid token' do
- expect(auth.authorise).to be_a(Ably::Models::Token)
- end
+ it 'returns a valid token' do
+ expect(auth.authorise).to be_a(Ably::Models::Token)
+ end
- it 'issues a new token if option :force => true' do
- expect { auth.authorise(force: true) }.to change { auth.current_token }
- end
+ it 'issues a new token if option :force => true' do
+ expect { auth.authorise(force: true) }.to change { auth.current_token }
end
+ end
- context 'with previous authorisation' do
- before do
- auth.authorise
- expect(auth.current_token).to_not be_expired
- end
+ context 'with previous authorisation' do
+ before do
+ auth.authorise
+ expect(auth.current_token).to_not be_expired
+ end
- it 'does not request a token if token is not expired' do
- expect(auth).to_not receive(:request_token)
- auth.authorise
- end
+ it 'does not request a token if current_token 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).to receive(:expired?).and_return(true)
- expect(auth).to receive(:request_token)
- expect { auth.authorise }.to change { auth.current_token }
- end
+ it 'requests a new token if token is expired' do
+ allow(auth.current_token).to receive(:expired?).and_return(true)
+ expect(auth).to receive(:request_token)
+ expect { auth.authorise }.to change { auth.current_token }
+ end
- it 'issues a new token if option :force => true' do
- expect { auth.authorise(force: true) }.to change { auth.current_token }
- end
+ it 'issues a new token if option :force => true' do
+ expect { auth.authorise(force: true) }.to change { auth.current_token }
end
end
- describe '#create_token_request' do
- let(:ttl) { 60 * 60 }
- let(:capability) { { :foo => ["publish"] } }
- let(:options) { Hash.new }
- subject { auth.create_token_request(options) }
+ it 'updates the persisted auth options thare are then used for subsequent authorise requests' do
+ expect(auth.options[:ttl]).to_not eql(26)
+ auth.authorise(ttl: 26)
+ expect(auth.options[:ttl]).to eql(26)
+ end
- it 'uses the key ID from the client' do
- expect(subject[:id]).to eql(key_id)
+ context 'with token_request_block' do
+ let(:client_id) { random_str }
+ let!(:token) do
+ auth.authorise do
+ @block_called ||= 0
+ @block_called += 1
+ auth.create_token_request(client_id: client_id)
+ end
end
- it 'uses the default TTL' do
- expect(subject[:ttl]).to eql(Ably::Models::Token::DEFAULTS[:ttl])
+ it 'calls the block' do
+ expect(@block_called).to eql(1)
end
- it 'uses the default capability' do
- expect(subject[:capability]).to eql(Ably::Models::Token::DEFAULTS[:capability].to_json)
+ it 'uses the token request returned from the block when requesting a new token' do
+ expect(token.client_id).to eql(client_id)
end
- it 'has a unique nonce' do
+ context 'for every subsequent #request_token' do
+ context 'without a provided block' do
+ it 'calls the originally provided block' do
+ auth.request_token
+ expect(@block_called).to eql(2)
+ end
+ end
+
+ context 'with a provided block' do
+ it 'does not call the originally provided block and calls the new #request_token block' do
+ auth.request_token { @request_block_called = true; auth.create_token_request }
+ expect(@block_called).to eql(1)
+ expect(@request_block_called).to eql(true)
+ end
+ end
+ end
+ end
+ end
+
+ describe '#create_token_request' do
+ let(:ttl) { 60 * 60 }
+ let(:capability) { { :foo => ["publish"] } }
+ let(:options) { Hash.new }
+ subject { auth.create_token_request(options) }
+
+ it 'uses the key ID from the client' do
+ expect(subject[:id]).to eql(key_id)
+ end
+
+ it 'uses the default TTL' do
+ expect(subject[:ttl]).to eql(Ably::Models::Token::DEFAULTS[:ttl])
+ end
+
+ it 'uses the default capability' do
+ expect(subject[:capability]).to eql(Ably::Models::Token::DEFAULTS[:capability].to_json)
+ end
+
+ context 'the nonce' do
+ it 'is unique for every request' do
unique_nonces = 100.times.map { auth.create_token_request[:nonce] }
expect(unique_nonces.uniq.length).to eql(100)
end
- it 'has a nonce of at least 16 characters' do
+ it 'is at least 16 characters' do
expect(subject[:nonce].length).to be >= 16
end
+ end
- %w(ttl capability nonce timestamp client_id).each do |attribute|
- context "with option :#{attribute}" do
- let(:option_value) { SecureRandom.random_number(1_000_000_000) }
- before do
- options[attribute.to_sym] = option_value
- end
- it "overrides default" do
- expect(subject[attribute.to_sym]).to eql(option_value)
- end
+ %w(ttl capability nonce timestamp client_id).each do |attribute|
+ context "with option :#{attribute}" do
+ let(:option_value) { random_int_str(1_000_000_000) }
+ before do
+ options[attribute.to_sym] = option_value
end
+ it "overrides default" do
+ expect(subject[attribute.to_sym].to_s).to eql(option_value.to_s)
+ end
end
+ end
- context 'invalid attributes' do
- let(:options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
- specify 'are ignored' do
- expect(subject.keys).to_not include(:is_not_used_by_token_request)
- expect(subject.keys).to include(:nonce)
- expect(subject[:nonce]).to eql('valid')
- end
+ context 'with additional invalid attributes' do
+ let(:options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
+ specify 'are ignored' do
+ expect(subject.keys).to_not include(:is_not_used_by_token_request)
+ expect(subject.keys).to include(:nonce)
+ expect(subject[:nonce]).to eql('valid')
end
+ end
- context 'missing key ID and/or secret' do
- let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) }
+ context 'when required fields are missing' do
+ let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) }
- it 'should raise an exception if key secret is missing' do
- expect { auth.create_token_request(key_id: 'id') }.to raise_error Ably::Exceptions::TokenRequestError
- end
+ it 'should raise an exception if key secret is missing' do
+ expect { auth.create_token_request(key_id: 'id') }.to raise_error Ably::Exceptions::TokenRequestError
+ end
- it 'should raise an exception if key id is missing' do
- expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError
- end
+ it 'should raise an exception if key id is missing' do
+ expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError
end
+ end
- context 'with :query_time option' do
- let(:time) { Time.now - 30 }
- let(:options) { { query_time: true } }
+ context 'with :query_time option' do
+ let(:time) { Time.now - 30 }
+ let(:options) { { query_time: true } }
- it 'queries the server for the time' do
- expect(client).to receive(:time).and_return(time)
- expect(subject[:timestamp]).to eql(time.to_i)
- end
+ it 'queries the server for the timestamp' do
+ expect(client).to receive(:time).and_return(time)
+ expect(subject[:timestamp]).to eql(time.to_i)
end
+ end
- context 'with :timestamp option' do
- let(:token_request_time) { Time.now + 5 }
- let(:options) { { timestamp: token_request_time } }
+ context 'with :timestamp option' do
+ let(:token_request_time) { Time.now + 5 }
+ let(:options) { { timestamp: token_request_time } }
- it 'uses the provided timestamp' do
- expect(subject[:timestamp]).to eql(token_request_time.to_i)
- end
+ it 'uses the provided timestamp in the token request' do
+ expect(subject[:timestamp]).to eql(token_request_time.to_i)
end
+ end
- context 'signing' do
- let(:options) do
- {
- id: SecureRandom.hex,
- ttl: SecureRandom.hex,
- capability: SecureRandom.hex,
- client_id: SecureRandom.hex,
- timestamp: SecureRandom.random_number(1_000_000_000),
- nonce: SecureRandom.hex
- }
- end
+ context 'signing' do
+ let(:options) do
+ {
+ id: random_str,
+ ttl: random_str,
+ capability: random_str,
+ client_id: random_str,
+ timestamp: random_int_str,
+ nonce: random_str
+ }
+ end
- it 'generates a valid HMAC' do
- hmac = hmac_for(options, key_secret)
- expect(subject[:mac]).to eql(hmac)
- end
+ it 'generates a valid HMAC' do
+ hmac = hmac_for(options, key_secret)
+ expect(subject[:mac]).to eql(hmac)
end
end
+ end
- context 'client with token authentication' do
- let(:capability) { { :foo => ["publish"] } }
+ context 'using token authentication' do
+ let(:capability) { { :foo => ["publish"] } }
- describe "with token_id argument" do
- let(:ttl) { 60 * 60 }
- let(:token) do
- auth.request_token(
- ttl: ttl,
- capability: capability
- )
- end
- let(:token_id) { token.id }
- let(:token_auth_client) do
- Ably::Rest::Client.new(token_id: token_id, environment: environment, protocol: protocol)
- end
+ describe 'with :token_id option' do
+ let(:ttl) { 60 * 60 }
+ let(:token) do
+ auth.request_token(
+ ttl: ttl,
+ capability: capability
+ )
+ end
+ let(:token_id) { token.id }
+ let(:token_auth_client) do
+ Ably::Rest::Client.new(token_id: token_id, environment: environment, protocol: protocol)
+ end
- it 'authenticates successfully' do
- expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy
- end
+ it 'authenticates successfully using the provided :token_id' do
+ expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy
+ end
- it 'disallows publishing on unspecified capability channels' do
- expect { token_auth_client.channel('bar').publish('event', 'data') }.to raise_error do |error|
- expect(error).to be_a(Ably::Exceptions::InvalidToken)
- expect(error.status).to eql(401)
- expect(error.code).to eql(40160)
- end
+ it 'disallows publishing on unspecified capability channels' do
+ expect { token_auth_client.channel('bar').publish('event', 'data') }.to raise_error do |error|
+ expect(error).to be_a(Ably::Exceptions::InvalidRequest)
+ expect(error.status).to eql(401)
+ expect(error.code).to eql(40160)
end
+ end
- it 'fails if timestamp is invalid' do
- expect { auth.request_token(timestamp: Time.now - 180) }.to raise_error do |error|
- expect(error).to be_a(Ably::Exceptions::InvalidToken)
- expect(error.status).to eql(401)
- expect(error.code).to eql(40101)
- end
+ it 'fails if timestamp is invalid' do
+ expect { auth.request_token(timestamp: Time.now - 180) }.to raise_error do |error|
+ expect(error).to be_a(Ably::Exceptions::InvalidRequest)
+ expect(error.status).to eql(401)
+ expect(error.code).to eql(40101)
end
end
- describe 'implicit through client id' do
- let(:client_id) { '999' }
- let(:client) do
- Ably::Rest::Client.new(api_key: api_key, client_id: client_id, environment: environment, protocol: protocol)
+ it 'cannot be renewed automatically' do
+ expect(token_auth_client.auth).to_not be_token_renewable
+ end
+ end
+
+ context 'when implicit as a result of using :client id' do
+ let(:client_id) { '999' }
+ let(:client) do
+ Ably::Rest::Client.new(api_key: api_key, client_id: client_id, environment: environment, protocol: protocol)
+ end
+ let(:token_id) { 'unique-token-id' }
+ let(:token_response) do
+ {
+ access_token: {
+ id: token_id
+ }
+ }.to_json
+ end
+
+ context 'and requests to the Ably server are mocked', :webmock do
+ let!(:request_token_stub) do
+ stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
+ to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' })
end
- let(:token_id) { 'unique-token-id' }
- let(:token_response) do
- {
- access_token: {
- id: token_id
- }
- }.to_json
+ let!(:publish_message_stub) do
+ stub_request(:post, "#{client.endpoint}/channels/foo/publish").
+ with(headers: { 'Authorization' => "Bearer #{encode64(token_id)}" }).
+ to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
end
- context 'stubbed', webmock: true do
- let!(:request_token_stub) do
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
- to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' })
- end
- let!(:publish_message_stub) do
- stub_request(:post, "#{client.endpoint}/channels/foo/publish").
- with(headers: { 'Authorization' => "Bearer #{encode64(token_id)}" }).
- to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
- end
-
- it 'will create a token request' do
- client.channel('foo').publish('event', 'data')
- expect(request_token_stub).to have_been_requested
- end
+ it 'will send a token request to the server' do
+ client.channel('foo').publish('event', 'data')
+ expect(request_token_stub).to have_been_requested
end
+ end
- context 'will create a token' do
- let(:token) { client.auth.current_token }
+ describe 'a token is created' do
+ let(:token) { client.auth.current_token }
- it 'before a request is made' do
- expect(token).to be_nil
- end
+ it 'before a request is made' do
+ expect(token).to be_nil
+ end
- it 'when a message is published' do
- expect(client.channel('foo').publish('event', 'data')).to be_truthy
- end
+ 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
- client.channel('foo').publish('event', 'data')
+ it 'with capability and TTL defaults' do
+ client.channel('foo').publish('event', 'data')
- expect(token).to be_a(Ably::Models::Token)
- capability_with_str_key = Ably::Models::Token::DEFAULTS[:capability]
- capability = Hash[capability_with_str_key.keys.map(&:to_sym).zip(capability_with_str_key.values)]
- expect(token.capability).to eq(capability)
- expect(token.expires_at.to_i).to be_within(2).of(Time.now.to_i + Ably::Models::Token::DEFAULTS[:ttl])
- expect(token.client_id).to eq(client_id)
- end
+ expect(token).to be_a(Ably::Models::Token)
+ capability_with_str_key = Ably::Models::Token::DEFAULTS[:capability]
+ capability = Hash[capability_with_str_key.keys.map(&:to_sym).zip(capability_with_str_key.values)]
+ expect(token.capability).to eq(capability)
+ expect(token.expires_at.to_i).to be_within(2).of(Time.now.to_i + Ably::Models::Token::DEFAULTS[:ttl])
+ expect(token.client_id).to eq(client_id)
end
end
end
end
- end
- def hmac_for(token_request, secret)
- text = token_request.values_at(
- :id,
- :ttl,
- :capability,
- :client_id,
- :timestamp,
- :nonce
- ).map { |t| "#{t}\n" }.join("")
+ context 'when using an :api_key and basic auth' do
+ specify '#using_token_auth? is false' do
+ expect(auth).to_not be_using_token_auth
+ end
- encode64(
- Digest::HMAC.digest(text, key_secret, Digest::SHA256)
- )
+ specify '#using_basic_auth? is true' do
+ expect(auth).to be_using_basic_auth
+ end
+ end
end
end