lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb in ably-rest-0.8.9 vs lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb in ably-rest-0.8.13

- old
+ new

@@ -252,9 +252,198 @@ end end end end end + + context 'with force: true to trigger an authentication upgrade' do + let(:rest_client) { Ably::Rest::Client.new(default_options) } + let(:client_publisher) { auto_close Ably::Realtime::Client.new(default_options) } + let(:basic_capability) { JSON.dump("foo" => ["subscribe"]) } + let(:basic_token_cb) { Proc.new do + rest_client.auth.create_token_request({ capability: basic_capability }) + end } + let(:upgraded_capability) { JSON.dump({ "foo" => ["subscribe", "publish"] }) } + let(:upgraded_token_cb) { Proc.new do + rest_client.auth.create_token_request({ capability: upgraded_capability }) + end } + let(:identified_token_cb) { Proc.new do + rest_client.auth.create_token_request({ client_id: 'bob' }) + end } + let(:downgraded_capability) { JSON.dump({ "bar" => ["subscribe"] }) } + let(:downgraded_token_cb) { Proc.new do + rest_client.auth.create_token_request({ capability: downgraded_capability }) + end } + + let(:client_options) { default_options.merge(auth_callback: basic_token_cb) } + + it 'forces the connection to disconnect and reconnect with a new token when in the CONNECTED state' do + client.connection.once(:connected) do + existing_token = client.auth.current_token_details + client.auth.authorise(nil, force: true) + client.connection.once(:disconnected) do + client.connection.once(:connected) do + expect(existing_token).to_not eql(client.auth.current_token_details) + stop_reactor + end + end + end + end + + it 'forces the connection to disconnect and reconnect with a new token when in the CONNECTING state' do + client.connection.once(:connecting) do + existing_token = client.auth.current_token_details + client.auth.authorise(nil, force: true) + client.connection.once(:disconnected) do + client.connection.once(:connected) do + expect(existing_token).to_not eql(client.auth.current_token_details) + stop_reactor + end + end + end + end + + context 'when client is identified' do + let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) } + + let(:basic_token_cb) { Proc.new do + rest_client.auth.create_token_request({ client_id: 'mike', capability: basic_capability }) + end } + + it 'transisitions the connection state to FAILED if the client_id changes' do + client.connection.once(:connected) do + client.auth.authorise(nil, auth_callback: identified_token_cb, force: true) + client.connection.once(:failed) do + expect(client.connection.error_reason.message).to match(/incompatible.*client ID/) + stop_reactor + end + end + end + end + + context 'when upgrading capabilities' do + let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :error) } + + it 'is allowed' do + client.connection.once(:connected) do + channel = client.channels.get('foo') + channel.publish('not-allowed').errback do |error| + expect(error.code).to eql(40160) + expect(error.message).to match(/permission denied/) + client.auth.authorise(nil, auth_callback: upgraded_token_cb, force: true) + client.connection.once(:connected) do + expect(client.connection.error_reason).to be_nil + channel.subscribe('allowed') do |message| + stop_reactor + end + channel.publish 'allowed' + end + end + end + end + end + + context 'when downgrading capabilities' do + let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) } + + it 'is allowed and channels are detached' do + client.connection.once(:connected) do + channel = client.channels.get('foo') + channel.attach do + client.auth.authorise(nil, auth_callback: downgraded_token_cb, force: true) + channel.once(:failed) do + expect(channel.error_reason.code).to eql(40160) + expect(channel.error_reason.message).to match(/Channel denied access/) + stop_reactor + end + end + end + end + end + + it 'ensures message delivery continuity whilst upgrading' do + received_messages = [] + subscriber_channel = client.channels.get('foo') + publisher_channel = client_publisher.channels.get('foo') + subscriber_channel.attach do + subscriber_channel.subscribe do |message| + received_messages << message + end + publisher_channel.attach do + publisher_channel.publish('foo') do + EventMachine.add_timer(2) do + expect(received_messages.length).to eql(1) + client.auth.authorise(nil, force: true) + client.connection.once(:disconnected) do + publisher_channel.publish('bar') do + expect(received_messages.length).to eql(1) + end + end + client.connection.once(:connected) do + EventMachine.add_timer(2) do + expect(received_messages.length).to eql(2) + stop_reactor + end + end + end + end + end + end + end + + it 'does not change the connection state if current connection state is closing' do + client.connection.once(:connected) do + client.connection.once(:closing) do + client.auth.authorise(nil, force: true) + client.connection.once(:connected) do + raise "Should not reconnect following auth force: true" + end + EventMachine.add_timer(4) do + expect(client.connection).to be_closed + stop_reactor + end + end + client.connection.close + end + end + + it 'does not change the connection state if current connection state is closed' do + client.connection.once(:connected) do + client.connection.once(:closed) do + client.auth.authorise(nil, force: true) + client.connection.once(:connected) do + raise "Should not reconnect following auth force: true" + end + EventMachine.add_timer(4) do + expect(client.connection).to be_closed + stop_reactor + end + end + client.connection.close + end + end + + context 'when state is failed' do + let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) } + + it 'does not change the connection state' do + client.connection.once(:connected) do + client.connection.once(:failed) do + client.auth.authorise(nil, force: true) + client.connection.once(:connected) do + raise "Should not reconnect following auth force: true" + end + EventMachine.add_timer(4) do + expect(client.connection).to be_failed + stop_reactor + end + end + protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Error.to_i) + client.connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message + end + end + end + end end context '#authorise_async' do it 'returns a token synchronously' do auth.authorise_sync(ttl: custom_ttl, client_id: custom_client_id).tap do |token_details|