Class: Net::IMAP::SASL::AuthenticationExchange

Inherits:
Object
  • Object
show all
Defined in:
lib/net/imap/sasl/authentication_exchange.rb

Overview

AuthenticationExchange is used internally by Net::IMAP#authenticate. But the API is still experimental, and may change.

TODO: catch exceptions in #process and send #cancel_response. TODO: raise an error if the command succeeds after being canceled. TODO: use with more clients, to verify the API can accommodate them. TODO: pass ClientAdapter#service to SASL.authenticator

An AuthenticationExchange represents a single attempt to authenticate a SASL client to a SASL server. It is created from a client adapter, a mechanism name, and a mechanism authenticator. When #authenticate is called, it will send the appropriate authenticate command to the server, returning the client response on success and raising an exception on failure.

In most cases, the client will not need to use SASL::AuthenticationExchange directly at all. Instead, use SASL::ClientAdapter#authenticate. If customizations are needed, the custom client adapter is probably the best place for that code.

def authenticate(...)
  MyClient::SASLAdapter.new(self).authenticate(...)
end

SASL::ClientAdapter#authenticate delegates to ::authenticate, like so:

def authenticate(...)
  sasl_adapter = MyClient::SASLAdapter.new(self)
  SASL::AuthenticationExchange.authenticate(sasl_adapter, ...)
end

::authenticate simply delegates to ::build and #authenticate, like so:

def authenticate(...)
  sasl_adapter = MyClient::SASLAdapter.new(self)
  SASL::AuthenticationExchange
    .build(sasl_adapter, ...)
    .authenticate
end

And ::build delegates to SASL.authenticator and ::new, like so:

def authenticate(mechanism, ...)
  sasl_adapter = MyClient::SASLAdapter.new(self)
  authenticator = SASL.authenticator(mechanism, ...)
  SASL::AuthenticationExchange
    .new(sasl_adapter, mechanism, authenticator)
    .authenticate
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client, mechanism, authenticator, sasl_ir: true) ⇒ AuthenticationExchange

Returns a new instance of AuthenticationExchange.



84
85
86
87
88
89
90
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 84

def initialize(client, mechanism, authenticator, sasl_ir: true)
  @client = client
  @mechanism = Authenticators.normalize_name(mechanism)
  @authenticator = authenticator
  @sasl_ir = sasl_ir
  @processed = false
end

Instance Attribute Details

#authenticatorObject (readonly)

Returns the value of attribute authenticator.



82
83
84
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 82

def authenticator
  @authenticator
end

#mechanismObject (readonly)

Returns the value of attribute mechanism.



82
83
84
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 82

def mechanism
  @mechanism
end

Class Method Details

.authenticateObject

Convenience method for build(...).authenticate

See also: SASL::ClientAdapter#authenticate



61
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 61

def self.authenticate(...) build(...).authenticate end

.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block) ⇒ Object

Convenience method to combine the creation of a new authenticator and a new Authentication exchange.

client must be an instance of SASL::ClientAdapter.

mechanism must be a SASL mechanism name, as a string or symbol.

sasl_ir allows or disallows sending an “initial response”, depending also on whether the server capabilities, mechanism authenticator, and client adapter all support it. Defaults to true.

mechanism, args, kwargs, and block are all forwarded to SASL.authenticator. Use the registry kwarg to override the global SASL::Authenticators registry.



77
78
79
80
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 77

def self.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block)
  authenticator = SASL.authenticator(mechanism, *args, **kwargs, &block)
  new(client, mechanism, authenticator, sasl_ir: sasl_ir)
end

Instance Method Details

#authenticateObject

Call #authenticate to execute an authentication exchange for #client using #authenticator. Authentication failures will raise an exception. Any exceptions other than those in RESPONSE_ERRORS will drop the connection.



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 96

def authenticate
  client.run_command(mechanism, initial_response) { process _1 }
    .tap { raise AuthenticationIncomplete, _1 unless done? }
rescue *client.response_errors
  raise # but don't drop the connection
rescue
  client.drop_connection
  raise
rescue Exception # rubocop:disable Lint/RescueException
  client.drop_connection!
  raise
end

#done?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 117

def done?
  authenticator.respond_to?(:done?) ? authenticator.done? : @processed
end

#send_initial_response?Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
115
# File 'lib/net/imap/sasl/authentication_exchange.rb', line 109

def send_initial_response?
  @sasl_ir &&
    authenticator.respond_to?(:initial_response?) &&
    authenticator.initial_response? &&
    client.sasl_ir_capable? &&
    client.auth_capable?(mechanism)
end