lib/mongo/uri.rb in mongo-2.12.4 vs lib/mongo/uri.rb in mongo-2.13.0.beta1

- old
+ new

@@ -1,6 +1,6 @@ -# Copyright (C) 2014-2019 MongoDB, Inc. +# Copyright (C) 2014-2020 MongoDB Inc. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # @@ -190,16 +190,17 @@ # Map of URI authentication mechanisms to Ruby driver mechanisms # # @since 2.0.0 AUTH_MECH_MAP = { 'GSSAPI' => :gssapi, + 'MONGODB-AWS' => :aws, # MONGODB-CR is deprecated and will be removed in driver version 3.0 'MONGODB-CR' => :mongodb_cr, 'MONGODB-X509' => :mongodb_x509, 'PLAIN' => :plain, 'SCRAM-SHA-1' => :scram, - 'SCRAM-SHA-256' => :scram256 + 'SCRAM-SHA-256' => :scram256, }.freeze # Options that are allowed to appear more than once in the uri. # # In order to follow the URI options spec requirement that all instances @@ -293,46 +294,11 @@ end if remaining.empty? raise_invalid_error!('No hosts in the URI') end parse!(remaining) - - # The URI options spec requires that we raise an error if there are conflicting values of - # 'tls' and 'ssl'. In order to fulfill this, we parse the values of each instance into an - # array; assuming all values in the array are the same, we replace the array with that value. - unless @uri_options[:ssl].nil? || @uri_options[:ssl].empty? - unless @uri_options[:ssl].uniq.length == 1 - raise_invalid_error_no_fmt!("all instances of 'tls' and 'ssl' must have the same value") - end - - @uri_options[:ssl] = @uri_options[:ssl].first - end - - # Check for conflicting TLS insecure options. - unless @uri_options[:ssl_verify].nil? - unless @uri_options[:ssl_verify_certificate].nil? - raise_invalid_error_no_fmt!("'tlsInsecure' and 'tlsAllowInvalidCertificates' cannot both be specified") - end - - unless @uri_options[:ssl_verify_hostname].nil? - raise_invalid_error_no_fmt!("tlsInsecure' and 'tlsAllowInvalidHostnames' cannot both be specified") - end - end - - # Since we know that the only URI option that sets :ssl_cert is "tlsCertificateKeyFile", any - # value set for :ssl_cert must also be set for :ssl_key. - if @uri_options[:ssl_cert] - @uri_options[:ssl_key] = @uri_options[:ssl_cert] - end - - if uri_options[:write_concern] && !uri_options[:write_concern].empty? - begin - WriteConcern.get(uri_options[:write_concern]) - rescue Error::InvalidWriteConcern => e - raise_invalid_error_no_fmt!("#{e.class}: #{e}") - end - end + validate_uri_options! end # Get the credentials provided in the URI. # # @example Get the credentials. @@ -433,13 +399,10 @@ end key, value = option_str.split('=', 2) if value.nil? raise_invalid_error!("Option #{key} has no value") end - if value.index('=') - raise_invalid_error!("Value for option #{key} contains the key/value delimiter (=): #{value}") - end key = decode(key) value = decode(value) add_uri_option(key, value, uri_options) end uri_options @@ -485,11 +448,11 @@ def decode(value) ::URI::DEFAULT_PARSER.unescape(value) end def encode(value) - ::URI.encode(value) + CGI.escape(value).gsub('+', '%20') end # Hash for storing map of URI option parameters to conversion strategies URI_OPTION_MAP = {} @@ -542,10 +505,11 @@ uri_option 'tlscertificatekeyfile', :ssl_cert uri_option 'tlscertificatekeyfilepassword', :ssl_key_pass_phrase uri_option 'tlsinsecure', :ssl_verify, :type => :inverse_bool # Topology options + uri_option 'directconnection', :direct_connection, type: :bool uri_option 'connect', :connect, type: :symbol # Auth Options uri_option 'authsource', :auth_source uri_option 'authmechanism', :auth_mech, :type => :auth_mech @@ -678,11 +642,11 @@ # @param value [ String ] The auth mechanism properties string. # # @return [ Hash ] The auth mechanism properties hash. def auth_mech_props(value) properties = hash_extractor('authMechanismProperties', value) - if properties[:canonicalize_host_name] + if properties && properties[:canonicalize_host_name] properties.merge!(canonicalize_host_name: properties[:canonicalize_host_name].downcase == 'true') end properties end @@ -853,27 +817,77 @@ # # @param value [ String ] The string to build a hash from. # # @return [ Hash ] The hash built from the string. def hash_extractor(name, value) - value.split(',').reduce({}) do |set, tag| + h = {} + value.split(',').each do |tag| k, v = tag.split(':') if v.nil? - log_warn("Invalid hash value for #{name}: #{value}") - return nil + log_warn("Invalid hash value for #{name}: key `#{k}` does not have a value: #{value}") end - set.merge(k.downcase.to_sym => v) + h[k.downcase.to_sym] = v end + h end # Extract values from the string and put them into an array. # # @param [ String ] value The string to build an array from. # # @return [ Array ] The array built from the string. def array(value) value.split(',') + end + + def validate_uri_options! + # The URI options spec requires that we raise an error if there are conflicting values of + # 'tls' and 'ssl'. In order to fulfill this, we parse the values of each instance into an + # array; assuming all values in the array are the same, we replace the array with that value. + unless uri_options[:ssl].nil? || uri_options[:ssl].empty? + unless uri_options[:ssl].uniq.length == 1 + raise_invalid_error_no_fmt!("all instances of 'tls' and 'ssl' must have the same value") + end + + uri_options[:ssl] = uri_options[:ssl].first + end + + # Check for conflicting TLS insecure options. + unless uri_options[:ssl_verify].nil? + unless uri_options[:ssl_verify_certificate].nil? + raise_invalid_error_no_fmt!("'tlsInsecure' and 'tlsAllowInvalidCertificates' cannot both be specified") + end + + unless uri_options[:ssl_verify_hostname].nil? + raise_invalid_error_no_fmt!("tlsInsecure' and 'tlsAllowInvalidHostnames' cannot both be specified") + end + end + + # Since we know that the only URI option that sets :ssl_cert is "tlsCertificateKeyFile", any + # value set for :ssl_cert must also be set for :ssl_key. + if uri_options[:ssl_cert] + uri_options[:ssl_key] = uri_options[:ssl_cert] + end + + if uri_options[:write_concern] && !uri_options[:write_concern].empty? + begin + WriteConcern.get(uri_options[:write_concern]) + rescue Error::InvalidWriteConcern => e + raise_invalid_error_no_fmt!("#{e.class}: #{e}") + end + end + + if uri_options[:direct_connection] + if uri_options[:connect] && uri_options[:connect].to_s != 'direct' + raise_invalid_error_no_fmt!("directConnection=true cannot be used with connect=#{uri_options[:connect]}") + end + if servers.length > 1 + raise_invalid_error_no_fmt!("directConnection=true cannot be used with multiple seeds") + end + elsif uri_options[:direct_connection] == false && uri_options[:connect].to_s == 'direct' + raise_invalid_error_no_fmt!("directConnection=false cannot be used with connect=direct") + end end end end require 'mongo/uri/srv_protocol'