Skip to content

Commit 33c6121

Browse files
authored
Fix RUBY-1948 Default authentication options are set on URI and not on client (#1550)
* remove duplicitious uri_spec tests * dont change uri spec * get all client integration tests working with default client options * cleaned up some code * uncomment another test * for now add back ssl_key default * change some variable names * make changes to client options to differentiate ssl_key and ssl_cert * update documentation * add comments back to uri * tests failing because variable doesnt exist * revert auth_source to nil on connection_spec * fix tests by being explicit about authentication mechanism * include auth mech when auth is set up * use the right environment variables * fix mmap tests * only specify auth mech for versions that enable scram256 * whoops wrong env variable * use connection to server to determine whether scram256 is supported * make sure to close client * fix connection spec * make comments more specific * committed something by mistake * update the comment in client registry * update comment again * scram256 was introduced in 4.0 * respond to pr feedback * comment * App metadata needs to take server options into account * Remove scram256 workaround * Clarify why sometimes features are instantiated once and sometimes twice
1 parent dafae82 commit 33c6121

File tree

9 files changed

+131
-115
lines changed

9 files changed

+131
-115
lines changed

.evergreen/functions.sh

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ set_env_vars() {
4646
AUTH=${AUTH:-noauth}
4747
SSL=${SSL:-nossl}
4848
MONGODB_URI=${MONGODB_URI:-}
49-
49+
5050
# drivers-evergreen-tools do not set tls parameter in URI when the
5151
# deployment uses TLS, repair this
5252
if test $SSL = ssl && ! echo $MONGODB_URI |grep -q tls=; then
@@ -58,20 +58,20 @@ set_env_vars() {
5858
MONGODB_URI="$MONGODB_URI/?tls=true"
5959
fi
6060
fi
61-
61+
6262
TOPOLOGY=${TOPOLOGY:-server}
6363
DRIVERS_TOOLS=${DRIVERS_TOOLS:-}
6464

6565
if [ "$AUTH" != "noauth" ]; then
6666
export ROOT_USER_NAME="bob"
6767
export ROOT_USER_PWD="pwd123"
6868
fi
69-
69+
7070
export MONGODB_URI
7171
export COMPRESSOR
72-
72+
7373
export CI=evergreen
74-
74+
7575
# JRUBY_OPTS were initially set for Mongoid
7676
export JRUBY_OPTS="--server -J-Xms512m -J-Xmx2G"
7777
}
@@ -81,7 +81,7 @@ setup_ruby() {
8181
echo "Empty RVM_RUBY, aborting"
8282
exit 2
8383
fi
84-
84+
8585
ls -l /opt
8686

8787
# Necessary for jruby
@@ -90,7 +90,7 @@ setup_ruby() {
9090
export JAVACMD=/opt/java/jdk8/bin/java
9191
export PATH=$PATH:/opt/java/jdk8/bin
9292
fi
93-
93+
9494
# ppc64le has it in a different place
9595
if test -z "$JAVACMD" && [ -f /usr/lib/jvm/java-1.8.0/bin/java ]; then
9696
export JAVACMD=/usr/lib/jvm/java-1.8.0/bin/java
@@ -104,22 +104,22 @@ setup_ruby() {
104104
export PATH=`pwd`/ruby-head/bin:`pwd`/ruby-head/lib/ruby/gems/2.6.0/bin:$PATH
105105
ruby --version
106106
ruby --version |grep dev
107-
107+
108108
#rvm reinstall $RVM_RUBY
109109
else
110110
if true; then
111-
111+
112112
# For testing toolchains:
113113
toolchain_url=https://s3.amazonaws.com//mciuploads/mongo-ruby-toolchain/`host_arch`/ce62fbb005213564a3da1041854da54df6615b2a/mongo_ruby_driver_toolchain_`host_arch |tr - _`_patch_ce62fbb005213564a3da1041854da54df6615b2a_5cfacdc857e85a3ef6647ad9_19_06_07_20_49_15.tar.gz
114114
curl -fL $toolchain_url |tar zxf -
115115
export PATH=`pwd`/rubies/$RVM_RUBY/bin:$PATH
116-
116+
117117
# Attempt to get bundler to report all errors - so far unsuccessful
118118
#curl -o bundler-openssl.diff https://github.com/bundler/bundler/compare/v2.0.1...p-mongo:report-errors.diff
119119
#find . -path \*/lib/bundler/fetcher.rb -exec patch {} bundler-openssl.diff \;
120-
120+
121121
else
122-
122+
123123
# Normal operation
124124
if ! test -d $HOME/.rubies/$RVM_RUBY/bin; then
125125
echo "Ruby directory does not exist: $HOME/.rubies/$RVM_RUBY/bin" 1>&2
@@ -132,9 +132,9 @@ setup_ruby() {
132132
exit 2
133133
fi
134134
export PATH=$HOME/.rubies/$RVM_RUBY/bin:$PATH
135-
135+
136136
fi
137-
137+
138138
ruby --version
139139

140140
# Ensure we're using the right ruby
@@ -178,7 +178,7 @@ kill_jruby() {
178178
prepare_server() {
179179
arch=$1
180180
version=$2
181-
181+
182182
url=http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-$arch-$version.tgz
183183
mongodb_dir="$MONGO_ORCHESTRATION_HOME"/mdb
184184
mkdir -p "$mongodb_dir"

docs/tutorials/ruby-driver-authentication.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ derived from the distinguished subject name of this certificate.
8888
This authentication method requires the use of SSL connections with
8989
certificate validation.
9090

91+
To authenticate the client, you will need a valid SSL certificate
92+
and private encryption key. These can be stored in separate files,
93+
or together in one file (in the PEM format). Even if the certificate
94+
and private key are stored in the same file, you must specify the path to
95+
that file by passing both the ``ssl_cert`` and ``ssl_key`` options
96+
to the client.
97+
9198
For more information about configuring X.509 authentication in MongoDB,
9299
see the :manual:`X.509 tutorial in the MongoDB Manual
93100
</tutorial/configure-x509/>`.
@@ -98,6 +105,7 @@ see the :manual:`X.509 tutorial in the MongoDB Manual
98105
auth_mech: :mongodb_x509,
99106
ssl: true,
100107
ssl_cert: '/path/to/client.pem',
108+
ssl_key: '/path/to/client.pem',
101109
ssl_ca_cert: '/path/to/ca.pem' )
102110

103111

lib/mongo/client.rb

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -398,18 +398,12 @@ def initialize(addresses_or_uri, options = nil)
398398
@srv_records = nil
399399
end
400400

401-
unless options[:retry_reads] == false
402-
options[:retry_reads] = true
403-
end
404-
unless options[:retry_writes] == false
405-
options[:retry_writes] = true
406-
end
407-
408401
# Special handling for sdam_proc as it is only used during client
409402
# construction
410403
sdam_proc = options.delete(:sdam_proc)
411404

412-
@options = validate_new_options!(Database::DEFAULT_OPTIONS.merge(options))
405+
options = default_options(options).merge(options)
406+
@options = validate_new_options!(options)
413407
=begin WriteConcern object support
414408
if @options[:write_concern].is_a?(WriteConcern::Base)
415409
# Cache the instance so that we do not needlessly reconstruct it.
@@ -811,6 +805,35 @@ def watch(pipeline = [], options = {})
811805

812806
private
813807

808+
# Generate default client options based on the URI and options
809+
# passed into the Client constructor.
810+
def default_options(options)
811+
Database::DEFAULT_OPTIONS.dup.tap do |default_options|
812+
if options[:auth_mech] || options[:user]
813+
default_options[:auth_source] = default_auth_source(options)
814+
end
815+
816+
if options[:auth_mech] == :gssapi
817+
default_options[:auth_mech_properties] = { service_name: 'mongodb' }
818+
end
819+
820+
default_options[:retry_reads] = true
821+
default_options[:retry_writes] = true
822+
end
823+
end
824+
825+
# Generate default auth source based on the URI and options
826+
def default_auth_source(options)
827+
case options[:auth_mech]
828+
when :gssapi, :mongodb_x509
829+
'$external'
830+
when :plain
831+
options[:database] || '$external'
832+
else
833+
options[:database] || Database::ADMIN
834+
end
835+
end
836+
814837
# If options[:session] is set, validates that session and returns it.
815838
# If deployment supports sessions, creates a new session and returns it.
816839
# The session is implicit unless options[:implicit] is given.

lib/mongo/uri.rb

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,10 @@ def self.get(string, opts = {})
241241
#
242242
# @since 2.0.0
243243
def client_options
244-
opts = default_client_options.merge(uri_options)
244+
opts = uri_options.tap do |opts|
245+
opts[:database] = @database if @database
246+
end
247+
245248
@user ? opts.merge(credentials) : opts
246249
end
247250

@@ -442,35 +445,6 @@ def parse_database!(string)
442445
decode(string) if string.length > 0
443446
end
444447

445-
def default_client_options
446-
opts = Options::Redacted.new(database: database)
447-
448-
if @uri_options[:auth_mech] || @user
449-
opts[:auth_source] = default_auth_source
450-
end
451-
452-
if @uri_options[:auth_mech] == :gssapi
453-
opts[:auth_mech_properties] = default_auth_mech_properties
454-
end
455-
456-
opts
457-
end
458-
459-
def default_auth_mech_properties
460-
{ service_name: 'mongodb' }
461-
end
462-
463-
def default_auth_source
464-
case @uri_options[:auth_mech]
465-
when :gssapi, :mongodb_x509
466-
'$external'
467-
when :plain
468-
@database || '$external'
469-
else
470-
@database || Database::ADMIN
471-
end
472-
end
473-
474448
def raise_invalid_error!(details)
475449
raise Error::InvalidURI.new(@string, details, FORMAT)
476450
end

spec/integration/client_options_spec.rb

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,19 @@
4949
end
5050
end
5151

52-
# TODO: get this test passing
53-
# context 'with client options' do
54-
# let(:client_opts) do
55-
# {
56-
# auth_mech: auth_mech_sym,
57-
# user: user,
58-
# password: pwd,
59-
# }
60-
# end
61-
62-
# it 'creates a client with default auth source' do
63-
# expect(client.options['auth_source']).to eq(default_auth_source)
64-
# end
65-
# end
52+
context 'with client options' do
53+
let(:client_opts) do
54+
{
55+
auth_mech: auth_mech_sym,
56+
user: user,
57+
password: pwd,
58+
}
59+
end
60+
61+
it 'creates a client with default auth source' do
62+
expect(client.options['auth_source']).to eq(default_auth_source)
63+
end
64+
end
6665
end
6766

6867
context 'where database is provided' do
@@ -77,21 +76,20 @@
7776
end
7877
end
7978

80-
# TODO: get this test passing
81-
# context 'with client options' do
82-
# let(:client_opts) do
83-
# {
84-
# auth_mech: auth_mech_sym,
85-
# user: user,
86-
# password: pwd,
87-
# database: database
88-
# }
89-
# end
90-
91-
# it 'creates a client with database as auth source' do
92-
# expect(client.options['auth_source']).to eq(database)
93-
# end
94-
# end
79+
context 'with client options' do
80+
let(:client_opts) do
81+
{
82+
auth_mech: auth_mech_sym,
83+
user: user,
84+
password: pwd,
85+
database: database
86+
}
87+
end
88+
89+
it 'creates a client with database as auth source' do
90+
expect(client.options['auth_source']).to eq(database)
91+
end
92+
end
9593
end
9694
end
9795

@@ -117,6 +115,7 @@
117115
auth_mech: auth_mech_sym,
118116
ssl: true,
119117
ssl_cert: cert_path,
118+
ssl_key: cert_path,
120119
ssl_ca_cert: ca_file_path,
121120
user: user,
122121
password: pwd
@@ -127,8 +126,7 @@
127126
expect(client.options[:ssl]).to be true
128127
expect(client.options[:ssl_cert]).to eq(cert_path)
129128
expect(client.options[:ssl_ca_cert]).to eq(ca_file_path)
130-
# TODO: get this expectation passing
131-
# expect(client.options[:ssl_key]).to eq(cert_path)
129+
expect(client.options[:ssl_key]).to eq(cert_path)
132130
end
133131
end
134132
end
@@ -245,20 +243,19 @@
245243
end
246244
end
247245

248-
# TODO: get this test passing
249-
# context 'with client options' do
250-
# let(:client_opts) do
251-
# {
252-
# auth_mech: :gssapi,
253-
# user: user,
254-
# password: pwd
255-
# }
256-
# end
257-
258-
# it 'sets default auth mech properties' do
259-
# expect(client.options[:auth_mech_properties]).to eq({ 'service_name' => 'mongodb' })
260-
# end
261-
# end
246+
context 'with client options' do
247+
let(:client_opts) do
248+
{
249+
auth_mech: :gssapi,
250+
user: user,
251+
password: pwd
252+
}
253+
end
254+
255+
it 'sets default auth mech properties' do
256+
expect(client.options[:auth_mech_properties]).to eq({ 'service_name' => 'mongodb' })
257+
end
258+
end
262259
end
263260

264261
context 'with PLAIN auth mechanism' do
@@ -312,10 +309,9 @@
312309
context 'with client options' do
313310
let(:client_opts) { { auth_mech: :mongodb_x509, user: user } }
314311

315-
# TODO: get this test passing
316-
# it 'sets default auth source' do
317-
# expect(client.options[:auth_source]).to eq('$external')
318-
# end
312+
it 'sets default auth source' do
313+
expect(client.options[:auth_source]).to eq('$external')
314+
end
319315

320316
context 'when username is not provided' do
321317
let(:client_opts) { { auth_mech: :mongodb_x509} }

spec/integration/connection_pool_populator_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
declare_topology_double
2323

24+
let(:app_metadata) do
25+
Mongo::Server::AppMetadata.new(options)
26+
end
27+
2428
let(:cluster) do
2529
double('cluster').tap do |cl|
2630
allow(cl).to receive(:topology).and_return(topology)

spec/integration/connection_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,10 @@
158158
RSpec::Mocks.with_temporary_scope do
159159
# now pretend an ismaster returned a different range
160160
features = Mongo::Server::Description::Features.new(0..3)
161-
# the second Features instantiation is for SDAM event publication
162-
expect(Mongo::Server::Description::Features).to receive(:new).twice.and_return(features)
161+
# One Features instantiation is for SDAM event publication, this
162+
# one always happens. The second one happens on servers
163+
# where we do not negotiate auth mechanism.
164+
expect(Mongo::Server::Description::Features).to receive(:new).at_least(:once).and_return(features)
163165

164166
connection = Mongo::Server::Connection.new(server, server.options)
165167
expect(connection.connect!).to be true

spec/mongo/server/connection_pool_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424

2525
declare_topology_double
2626

27+
let(:app_metadata) do
28+
Mongo::Server::AppMetadata.new(server_options)
29+
end
30+
2731
let(:cluster) do
2832
double('cluster').tap do |cl|
2933
allow(cl).to receive(:topology).and_return(topology)

0 commit comments

Comments
 (0)