Class: Mongo::Server::PendingConnection Private
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
Forwardable,
ConnectionBase ,
Forwardable,
ConnectionCommon
|
|
Instance Chain:
|
|
Inherits: |
Mongo::Server::ConnectionBase
|
Defined in: | lib/mongo/server/pending_connection.rb |
Overview
This class encapsulates connections during handshake and authentication.
Constant Summary
ConnectionCommon
- Inherited
::Mongo::Loggable
- Included
ConnectionBase
- Inherited
DEFAULT_MAX_BSON_OBJECT_SIZE, MAX_BSON_COMMAND_OVERHEAD, REDUCED_MAX_BSON_SIZE
Class Method Summary
- .new(socket, server, monitoring, options = {}) ⇒ PendingConnection constructor Internal use only
Instance Attribute Summary
- #id ⇒ Integer readonly Internal use only
ConnectionBase
- Inherited
#description | Returns the server description for this connection, derived from the hello response for the handshake performed on this connection. |
#options, #server |
::Mongo::Monitoring::Publishable
- Included
ConnectionCommon
- Inherited
#compressor | The compressor negotiated during the handshake for this connection, if any. |
#connected? | Determine if the connection is currently connected. |
#pid, #socket |
Instance Method Summary
- #handshake_and_authenticate! Internal use only
- #authenticate!(speculative_auth_client_nonce: nil, speculative_auth_mech: nil, speculative_auth_result: nil) private Internal use only
- #default_mechanism private Internal use only
- #ensure_connected {|@socket| ... } private Internal use only
-
#get_handshake_response(hello_command) ⇒ Mongo::Protocol::Reply
private
Internal use only
Sends the hello command to the server, then receive and deserialize the response.
- #handshake!(speculative_auth_doc: nil) ⇒ BSON::Document private Internal use only
-
#post_handshake(response, average_rtt) ⇒ Server::Description
private
Internal use only
This is a separate method to keep the nesting level down.
-
#resolved_user(speculative_auth_mech: nil) ⇒ Auth::User
private
Internal use only
The user as going to be used for authentication.
ConnectionBase
- Inherited
#app_metadata, | |
#dispatch | Dispatch a single message to the connection. |
#generation |
|
#service_id, #deliver, #serialize |
::Mongo::Monitoring::Publishable
- Included
#publish_cmap_event, #publish_event, #publish_sdam_event, #command_completed, #command_failed, #command_started, #command_succeeded, #duration |
::Mongo::Loggable
- Included
#log_debug | Convenience method to log debug messages with the standard prefix. |
#log_error | Convenience method to log error messages with the standard prefix. |
#log_fatal | Convenience method to log fatal messages with the standard prefix. |
#log_info | Convenience method to log info messages with the standard prefix. |
#log_warn | Convenience method to log warn messages with the standard prefix. |
#logger | Get the logger instance. |
#_mongo_log_prefix, #format_message |
ConnectionCommon
- Inherited
#handshake_command | Build a command that should be used for connection handshake. |
#handshake_document | Build a document that should be used for connection handshake. |
#add_server_diagnostics | Yields to the block and, if the block raises an exception, adds a note to the exception with the address of the specified server. |
#ensure_connected, #set_compressor!, #ssl_options |
Instance Attribute Details
#id ⇒ Integer
(readonly)
# File 'lib/mongo/server/pending_connection.rb', line 38
attr_reader :id
Instance Method Details
#authenticate!(speculative_auth_client_nonce: nil, speculative_auth_mech: nil, speculative_auth_result: nil) (private)
# File 'lib/mongo/server/pending_connection.rb', line 185
def authenticate!( speculative_auth_client_nonce: nil, speculative_auth_mech: nil, speculative_auth_result: nil ) if [:user] || [:auth_mech] @server.handle_auth_failure! do begin auth = Auth.get( resolved_user(speculative_auth_mech: speculative_auth_mech), self, speculative_auth_client_nonce: speculative_auth_client_nonce, speculative_auth_result: speculative_auth_result, ) auth.login rescue => exc msg = "Failed to authenticate to #{address}" Utils.warn_bg_exception(msg, exc, logger: [:logger], log_prefix: [:log_prefix], bg_error_backtrace: [:bg_error_backtrace], ) raise end end end end
#default_mechanism (private)
# File 'lib/mongo/server/pending_connection.rb', line 284
def default_mechanism if description.nil? raise Mongo::Error, 'Trying to query default mechanism when handshake has not completed' end if description.features.scram_sha_1_enabled? if @sasl_supported_mechanisms&.include?('SCRAM-SHA-256') :scram256 else :scram end else :mongodb_cr end end
#ensure_connected {|@socket| ... } (private)
# File 'lib/mongo/server/pending_connection.rb', line 213
def ensure_connected yield @socket end
#get_handshake_response(hello_command) ⇒ Mongo::Protocol::Reply (private)
Sends the hello command to the server, then receive and deserialize the response.
This method is extracted to be mocked in the tests.
# File 'lib/mongo/server/pending_connection.rb', line 122
def get_handshake_response(hello_command) @server.round_trip_time_averager.measure do add_server_diagnostics do socket.write(hello_command.serialize.to_s) Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE) end end end
#handshake!(speculative_auth_doc: nil) ⇒ BSON::Document
(private)
# File 'lib/mongo/server/pending_connection.rb', line 136
def handshake!(speculative_auth_doc: nil) unless socket raise Error::InternalDriverError, "Cannot handshake because there is no usable socket (for #{address})" end hello_command = handshake_command( handshake_document( , speculative_auth_doc: speculative_auth_doc, load_balancer: server.load_balancer?, server_api: [:server_api] ) ) doc = nil @server.handle_handshake_failure! do begin response = get_handshake_response(hello_command) result = Operation::Result.new([response]) result.validate! doc = result.documents.first rescue => exc msg = "Failed to handshake with #{address}" Utils.warn_bg_exception(msg, exc, logger: [:logger], log_prefix: [:log_prefix], bg_error_backtrace: [:bg_error_backtrace], ) raise end end if @server.force_load_balancer? doc['serviceId'] ||= "fake:#{rand(2**32-1)+1}" end post_handshake(doc, @server.round_trip_time_averager.average_round_trip_time) doc end
#handshake_and_authenticate!
# File 'lib/mongo/server/pending_connection.rb', line 40
def handshake_and_authenticate! speculative_auth_doc = nil if [:user] || [:auth_mech] # To create an Auth instance, we need to specify the mechanism, # but at this point we don't know the mechanism that ultimately # will be used (since this depends on the data returned by # the handshake, specifically server version). # However, we know that only 4.4+ servers support speculative # authentication, and those servers also generally support # SCRAM-SHA-256. We expect that user accounts created for 4.4+ # servers would generally allow SCRAM-SHA-256 authentication; # user accounts migrated from pre-4.4 servers may only allow # SCRAM-SHA-1. The use of SCRAM-SHA-256 by default is thus # sensible, and it is also mandated by the speculative auth spec. # If no mechanism was specified and we are talking to a 3.0+ # server, we'll send speculative auth document, the server will # ignore it and we'll perform authentication using explicit # command after having defaulted the mechanism later to CR. # If no mechanism was specified and we are talking to a 4.4+ # server and the user account doesn't allow SCRAM-SHA-256, we will # authenticate in a separate command with SCRAM-SHA-1 after # going through SCRAM mechanism negotiation. = Options::Redacted.new(:auth_mech => :scram256) speculative_auth_user = Auth::User.new( .merge( )) speculative_auth = Auth.get(speculative_auth_user, self) speculative_auth_doc = speculative_auth.conversation.speculative_auth_document end result = handshake!(speculative_auth_doc: speculative_auth_doc) if description.unknown? raise Error::InternalDriverError, "Connection description cannot be unknown after successful handshake: #{description.inspect}" end begin if speculative_auth_doc && (speculative_auth_result = result['speculativeAuthenticate']) unless description.features.scram_sha_1_enabled? raise Error::InvalidServerAuthResponse, "Speculative auth succeeded on a pre-3.0 server" end case speculative_auth_user.mechanism when :mongodb_x509 # Done # We default auth mechanism to scram256, but if user specified # scram explicitly we may be able to authenticate speculatively # with scram. when :scram, :scram256 authenticate!( speculative_auth_client_nonce: speculative_auth.conversation.client_nonce, speculative_auth_mech: speculative_auth_user.mechanism, speculative_auth_result: speculative_auth_result, ) else raise Error::InternalDriverError, "Speculative auth unexpectedly succeeded for mechanism #{speculative_auth_user.mechanism.inspect}" end elsif !description.arbiter? authenticate! end rescue Mongo::Error, Mongo::Error::AuthError => exc exc.service_id = service_id raise end if description.unknown? raise Error::InternalDriverError, "Connection description cannot be unknown after successful authentication: #{description.inspect}" end if server.load_balancer? && !description.mongos? raise Error::BadLoadBalancerTarget, "Load-balanced operation requires being connected a mongos, but the server at #{address.seed} reported itself as #{description.server_type.to_s.gsub('_', ' ')}" end end
#post_handshake(response, average_rtt) ⇒ Server::Description (private)
This is a separate method to keep the nesting level down.
# File 'lib/mongo/server/pending_connection.rb', line 221
def post_handshake(response, average_rtt) if response["ok"] == 1 # Auth mechanism is entirely dependent on the contents of # hello response *for this connection*. # Hello received by the monitoring connection should advertise # the same wire protocol, but if it doesn't, we use whatever # the monitoring connection advertised for filling out the # server description and whatever the non-monitoring connection # (that's this one) advertised for performing auth on that # connection. @sasl_supported_mechanisms = response['saslSupportedMechs'] set_compressor!(response) else @sasl_supported_mechanisms = nil end @description = Description.new( address, response, average_round_trip_time: average_rtt, load_balancer: server.load_balancer?, force_load_balancer: [:connect] == :load_balanced, ).tap do |new_description| @server.cluster.run_sdam_flow(@server.description, new_description) end end
#resolved_user(speculative_auth_mech: nil) ⇒ Auth::User (private)
The user as going to be used for authentication. This user has the auth mechanism set and, if necessary, auth source.
# File 'lib/mongo/server/pending_connection.rb', line 255
def resolved_user(speculative_auth_mech: nil) @resolved_user ||= begin unless [:user] || [:auth_mech] raise Mongo::Error, 'No authentication information specified in the client' end = Options::Redacted.new( # When speculative auth is performed, we always use SCRAM-SHA-256. # At the same time we perform SCRAM mechanism negotiation in the # hello request. # If the credentials we are trying to authenticate with do not # map to an existing user, SCRAM mechanism negotiation will not # return anything which would cause the driver to use # SCRAM-SHA-1. However, on 4.4+ servers speculative auth would # succeed (technically just the first round-trip, not the entire # authentication flow) and we would be continuing it here; # in this case, we must use SCRAM-SHA-256 as the mechanism since # that is what the conversation was started with, even though # SCRAM mechanism negotiation did not return SCRAM-SHA-256 as a # valid mechanism to use for these credentials. :auth_mech => speculative_auth_mech || default_mechanism, ).merge( ) if [:auth_mech] == :mongodb_x509 [:auth_source] = '$external' end Auth::User.new( ) end end