lib/submodules/ably-ruby/lib/ably/realtime/auth.rb in ably-rest-0.9.3 vs lib/submodules/ably-ruby/lib/ably/realtime/auth.rb in ably-rest-1.0.0

- old
+ new

@@ -42,17 +42,19 @@ def_delegators :auth_sync, :current_token_details, :token def_delegators :auth_sync, :key, :key_name, :key_secret, :options, :auth_options, :token_params def_delegators :auth_sync, :using_basic_auth?, :using_token_auth? def_delegators :auth_sync, :token_renewable?, :authentication_security_requirements_met? def_delegators :client, :logger + def_delegators :client, :connection def initialize(client) @client = client @auth_sync = client.rest_client.auth end - # Ensures valid auth credentials are present for the library instance. This may rely on an already-known and valid token, and will obtain a new token if necessary. + # For new connections, ensures valid auth credentials are present for the library instance. This may rely on an already-known and valid token, and will obtain a new token if necessary. + # If a connection is already established, the connection will be upgraded with a new token # # In the event that a new token request is made, the provided options are used # # @param (see Ably::Auth#authorize) # @option (see Ably::Auth#authorize) @@ -66,45 +68,90 @@ # client.auth.authorize do |token_details| # token_details #=> Ably::Models::TokenDetails # end # def authorize(token_params = nil, auth_options = nil, &success_callback) - async_wrap(success_callback) do - auth_sync.authorize(token_params, auth_options, &method(:upgrade_authentication_block).to_proc) - end.tap do |deferrable| - deferrable.errback do |error| - client.connection.transition_state_machine :failed, reason: error if error.kind_of?(Ably::Exceptions::IncompatibleClientId) + Ably::Util::SafeDeferrable.new(logger).tap do |authorize_method_deferrable| + # Wrap the sync authorize method and wait for the result from the deferrable + async_wrap do + authorize_sync(token_params, auth_options) + end.tap do |auth_operation| + # Authorize operation succeeded and we have a new token, now let's perform inline authentication + auth_operation.callback do |token| + case connection.state.to_sym + when :initialized, :disconnected, :suspended, :closed, :closing, :failed + connection.connect + when :connected + perform_inline_auth token + when :connecting + # Fail all current connection attempts and try again with the new token, see #RTC8b + connection.manager.release_and_establish_new_transport + else + logger.fatal { "Auth#authorize: unsupported state #{connection.state}" } + authorize_method_deferrable.fail Ably::Exceptions::InvalidState.new("Unsupported state #{connection.state} for Auth#authorize") + next + end + + # Indicate success or failure based on response from realtime, see #RTC8b1 + auth_deferrable_resolved = false + + connection.unsafe_once(:connected, :update) do + auth_deferrable_resolved = true + authorize_method_deferrable.succeed token + end + connection.unsafe_once(:suspended, :closed, :failed) do |state_change| + auth_deferrable_resolved = true + authorize_method_deferrable.fail state_change.reason + end + end + + # Authorize failed, likely due to auth_url or auth_callback failing + auth_operation.errback do |error| + client.connection.transition_state_machine :failed, reason: error if error.kind_of?(Ably::Exceptions::IncompatibleClientId) + authorize_method_deferrable.fail error + end end + + # Call the block provided to this method upon success of this deferrable + authorize_method_deferrable.callback do |token| + yield token if block_given? + end end end # @deprecated Use {#authorize} instead def authorise(*args, &block) - logger.warn "Auth#authorise is deprecated and will be removed in 1.0. Please use Auth#authorize instead" + logger.warn { "Auth#authorise is deprecated and will be removed in 1.0. Please use Auth#authorize instead" } authorize(*args, &block) end # Synchronous version of {#authorize}. See {Ably::Auth#authorize} for method definition + # Please note that authorize_sync will however not upgrade the current connection's token as this requires + # an synchronous operation to send the new authentication details to Ably over a realtime connection + # # @param (see Ably::Auth#authorize) # @option (see Ably::Auth#authorize) # @return [Ably::Models::TokenDetails] # def authorize_sync(token_params = nil, auth_options = nil) - auth_sync.authorize(token_params, auth_options, &method(:upgrade_authentication_block).to_proc) + @authorization_in_flight = true + auth_sync.authorize(token_params, auth_options) + ensure + @authorization_in_flight = false end + # @api private + def authorization_in_flight? + @authorization_in_flight + end + # @deprecated Use {#authorize_sync} instead def authorise_sync(*args) - logger.warn "Auth#authorise_sync is deprecated and will be removed in 1.0. Please use Auth#authorize_sync instead" + logger.warn { "Auth#authorise_sync is deprecated and will be removed in 1.0. Please use Auth#authorize_sync instead" } authorize_sync(*args) end - # def_delegator :auth_sync, :request_token, :request_token_sync - # def_delegator :auth_sync, :create_token_request, :create_token_request_sync - # def_delegator :auth_sync, :auth_header, :auth_header_sync - # def_delegator :auth_sync, :auth_params, :auth_params_sync - # Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests # # @param (see Ably::Auth#request_token) # @option (see Ably::Auth#request_token) # @@ -184,11 +231,20 @@ # # @return [Ably::Util::SafeDeferrable] # @yield [Hash] Auth params for a new Realtime connection # def auth_params(&success_callback) - async_wrap(success_callback) do + fail_callback = Proc.new do |error, deferrable| + logger.error { "Failed to authenticate: #{error}" } + if error.kind_of?(Ably::Exceptions::BaseAblyException) + # Use base exception if it exists carrying forward the status codes + deferrable.fail Ably::Exceptions::AuthenticationFailed.new(error.message, nil, nil, error) + else + deferrable.fail Ably::Exceptions::AuthenticationFailed.new(error.message, 500, 80019) + end + end + async_wrap(success_callback, fail_callback) do auth_params_sync end end # Synchronous version of {#auth_params}. See {Ably::Auth#auth_params} for method definition @@ -207,25 +263,17 @@ def client @client end - # If authorize is called with true, this block is executed so that it - # can perform the authentication upgrade - def upgrade_authentication_block(new_token) - # This block is called if the authorisation was forced - if client.connection.connected? || client.connection.connecting? - logger.debug "Realtime::Auth - authorize was called so forcibly disconnecting transport to initiate auth upgrade" - block = Proc.new do - if client.connection.transport - logger.debug "Realtime::Auth - current transport disconnected" - client.connection.transport.disconnect - else - EventMachine.add_timer(0.1, &block) - end - end - block.call - end + # Sends an AUTH ProtocolMessage on the existing connection triggering + # an inline AUTH process, see #RTC8a + def perform_inline_auth(token) + logger.debug { "Performing inline AUTH with Ably using token #{token}" } + connection.send_protocol_message( + action: Ably::Models::ProtocolMessage::ACTION.Auth.to_i, + auth: { access_token: token.token } + ) end end end end