class Mongo::Auth::Base

Base class for authenticators.

Each authenticator is instantiated for authentication over a particular connection.

@api private

Attributes

connection[R]

@return [ Mongo::Connection ] The connection to authenticate over.

user[R]

@return [ Mongo::Auth::User ] The user to authenticate.

Public Class Methods

new(user, connection, **opts) click to toggle source

Initializes the authenticator.

@param [ Auth::User ] user The user to authenticate. @param [ Mongo::Connection ] connection The connection to authenticate

over.
# File lib/mongo/auth/base.rb, line 39
def initialize(user, connection, **opts)
  @user = user
  @connection = connection
end

Public Instance Methods

conversation() click to toggle source
# File lib/mongo/auth/base.rb, line 44
def conversation
  @conversation ||= self.class.const_get(:Conversation).new(user, connection)
end

Private Instance Methods

converse_1_step(connection, conversation) click to toggle source

Performs a single-step conversation on the given connection.

# File lib/mongo/auth/base.rb, line 51
def converse_1_step(connection, conversation)
  msg = conversation.start(connection)
  dispatch_msg(connection, conversation, msg)
end
converse_2_step(connection, conversation) click to toggle source

Performs a two-step conversation on the given connection.

The implementation is very similar to converse_multi_step, but conversations using this method do not involve the server replying with {done: true} to indicate the end of the conversation.

# File lib/mongo/auth/base.rb, line 61
def converse_2_step(connection, conversation)
  msg = conversation.start(connection)
  reply_document = dispatch_msg(connection, conversation, msg)
  msg = conversation.continue(reply_document, connection)
  dispatch_msg(connection, conversation, msg)
end
converse_multi_step(connection, conversation, speculative_auth_result: nil ) click to toggle source

Performs the variable-length SASL conversation on the given connection.

@param [ Server::Connection ] connection The connection. @param [ Auth::*::Conversation ] conversation The conversation. @param [ BSON::Document | nil ] speculative_auth_result The

value of speculativeAuthenticate field of hello response of
the handshake on the specified connection.
# File lib/mongo/auth/base.rb, line 75
def converse_multi_step(connection, conversation,
  speculative_auth_result: nil
)
  # Although the SASL conversation in theory can have any number of
  # steps, all defined authentication methods have a predefined number
  # of steps, and therefore all of our authenticators have a fixed set
  # of methods that generate payloads with one method per step.
  # We support a maximum of 3 total exchanges (start, continue and
  # finalize) and in practice the first two exchanges always happen.
  if speculative_auth_result
    reply_document = speculative_auth_result
  else
    msg = conversation.start(connection)
    reply_document = dispatch_msg(connection, conversation, msg)
  end
  msg = conversation.continue(reply_document, connection)
  reply_document = dispatch_msg(connection, conversation, msg)
  conversation.process_continue_response(reply_document)
  unless reply_document[:done]
    msg = conversation.finalize(connection)
    reply_document = dispatch_msg(connection, conversation, msg)
  end
  unless reply_document[:done]
    raise Error::InvalidServerAuthResponse,
      'Server did not respond with {done: true} after finalizing the conversation'
  end
  reply_document
end
dispatch_msg(connection, conversation, msg) click to toggle source
# File lib/mongo/auth/base.rb, line 104
def dispatch_msg(connection, conversation, msg)
  context = Operation::Context.new(options: {
    server_api: connection.options[:server_api],
  })
  if server_api = context.server_api
    msg = msg.maybe_add_server_api(server_api)
  end
  reply = connection.dispatch([msg], context)
  reply_document = reply.documents.first
  validate_reply!(connection, conversation, reply_document)
  connection_global_id = if connection.respond_to?(:global_id)
    connection.global_id
  else
    nil
  end
  result = Operation::Result.new(reply, connection.description, connection_global_id)
  connection.update_cluster_time(result)
  reply_document
end
validate_reply!(connection, conversation, doc) click to toggle source

Checks whether reply is successful (i.e. has {ok: 1} set) and raises Unauthorized if not.

# File lib/mongo/auth/base.rb, line 126
def validate_reply!(connection, conversation, doc)
  if doc[:ok] != 1
    message = Error::Parser.build_message(
      code: doc[:code],
      code_name: doc[:codeName],
      message: doc[:errmsg],
    )

    raise Unauthorized.new(user,
      used_mechanism: self.class.const_get(:MECHANISM),
      message: message,
      server: connection.server,
      code: doc[:code]
    )
  end
end