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, minimum_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, | |
#check_timeout! | If timeoutMS is set for the operation context, checks whether there is enough time left to send the corresponding message to the server (remaining timeout is bigger than minimum round trip time for the server). |
#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 189
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 288
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 217
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_calculator.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_calculator.average_round_trip_time, @server.round_trip_time_calculator.minimum_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, minimum_rtt) ⇒ Server::Description (private)
This is a separate method to keep the nesting level down.
# File 'lib/mongo/server/pending_connection.rb', line 225
def post_handshake(response, average_rtt, minimum_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 259
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