mongo 2.11.0.rc0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +2 -0
  5. data/lib/mongo/auth.rb +11 -2
  6. data/lib/mongo/auth/cr/conversation.rb +1 -1
  7. data/lib/mongo/auth/ldap/conversation.rb +1 -1
  8. data/lib/mongo/auth/scram/conversation.rb +4 -1
  9. data/lib/mongo/auth/user.rb +15 -1
  10. data/lib/mongo/auth/user/view.rb +10 -4
  11. data/lib/mongo/auth/x509.rb +11 -1
  12. data/lib/mongo/auth/x509/conversation.rb +15 -6
  13. data/lib/mongo/background_thread.rb +28 -13
  14. data/lib/mongo/client.rb +23 -15
  15. data/lib/mongo/collection/view/change_stream.rb +5 -1
  16. data/lib/mongo/collection/view/readable.rb +5 -2
  17. data/lib/mongo/collection/view/writable.rb +3 -1
  18. data/lib/mongo/cursor/builder/get_more_command.rb +4 -1
  19. data/lib/mongo/cursor/builder/kill_cursors_command.rb +16 -5
  20. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  21. data/lib/mongo/cursor/builder/op_kill_cursors.rb +17 -5
  22. data/lib/mongo/error/operation_failure.rb +3 -3
  23. data/lib/mongo/monitoring/command_log_subscriber.rb +5 -3
  24. data/lib/mongo/monitoring/event/command_started.rb +13 -3
  25. data/lib/mongo/monitoring/publishable.rb +4 -2
  26. data/lib/mongo/operation/create_user/command.rb +1 -0
  27. data/lib/mongo/operation/remove_user/command.rb +1 -0
  28. data/lib/mongo/operation/update_user/command.rb +1 -0
  29. data/lib/mongo/protocol/get_more.rb +2 -1
  30. data/lib/mongo/protocol/kill_cursors.rb +6 -13
  31. data/lib/mongo/protocol/serializers.rb +10 -4
  32. data/lib/mongo/retryable.rb +1 -1
  33. data/lib/mongo/server/connection.rb +6 -2
  34. data/lib/mongo/server/connection_base.rb +2 -1
  35. data/lib/mongo/server/monitor.rb +1 -1
  36. data/lib/mongo/server/pending_connection.rb +6 -0
  37. data/lib/mongo/socket/ssl.rb +1 -1
  38. data/lib/mongo/uri.rb +5 -41
  39. data/lib/mongo/version.rb +1 -1
  40. data/mongo.gemspec +11 -2
  41. data/spec/README.md +105 -9
  42. data/spec/USERS.md +72 -0
  43. data/spec/integration/auth_spec.rb +20 -6
  44. data/spec/integration/client_construction_spec.rb +3 -1
  45. data/spec/integration/client_options_spec.rb +437 -0
  46. data/spec/integration/command_monitoring_spec.rb +4 -1
  47. data/spec/integration/connection_pool_populator_spec.rb +4 -0
  48. data/spec/integration/connection_spec.rb +4 -2
  49. data/spec/integration/cursor_reaping_spec.rb +1 -1
  50. data/spec/integration/get_more_spec.rb +32 -0
  51. data/spec/integration/retryable_errors_spec.rb +99 -0
  52. data/spec/integration/retryable_writes_errors_spec.rb +11 -10
  53. data/spec/lite_spec_helper.rb +2 -1
  54. data/spec/mongo/auth/scram_spec.rb +1 -0
  55. data/spec/mongo/auth/user/view_spec.rb +102 -1
  56. data/spec/mongo/auth/user_spec.rb +56 -15
  57. data/spec/mongo/auth/x509_spec.rb +31 -1
  58. data/spec/mongo/bulk_write_spec.rb +2 -2
  59. data/spec/mongo/collection/view/change_stream_spec.rb +2 -2
  60. data/spec/mongo/collection/view/readable_spec.rb +8 -4
  61. data/spec/mongo/cursor/builder/get_more_command_spec.rb +4 -2
  62. data/spec/mongo/cursor/builder/op_get_more_spec.rb +4 -2
  63. data/spec/mongo/cursor_spec.rb +3 -3
  64. data/spec/mongo/retryable_spec.rb +31 -52
  65. data/spec/mongo/server/connection_auth_spec.rb +3 -0
  66. data/spec/mongo/server/connection_pool_spec.rb +4 -0
  67. data/spec/mongo/server/connection_spec.rb +12 -4
  68. data/spec/mongo/server/monitor_spec.rb +19 -1
  69. data/spec/mongo/socket/ssl_spec.rb +1 -1
  70. data/spec/mongo/uri/srv_protocol_spec.rb +0 -13
  71. data/spec/mongo/uri_option_parsing_spec.rb +0 -8
  72. data/spec/mongo/uri_spec.rb +6 -20
  73. data/spec/runners/connection_string.rb +116 -0
  74. data/spec/runners/read_write_concern_document.rb +67 -0
  75. data/spec/spec_tests/change_streams_spec.rb +17 -2
  76. data/spec/spec_tests/connection_string_spec.rb +2 -59
  77. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +3 -3
  78. data/spec/spec_tests/data/change_streams/change-streams.yml +88 -20
  79. data/spec/spec_tests/data/cmap/connection-must-have-id.yml +6 -0
  80. data/spec/spec_tests/data/cmap/connection-must-order-ids.yml +6 -0
  81. data/spec/spec_tests/data/cmap/pool-checkin-destroy-closed.yml +3 -0
  82. data/spec/spec_tests/data/cmap/pool-checkin-destroy-stale.yml +3 -0
  83. data/spec/spec_tests/data/cmap/pool-checkin-make-available.yml +3 -0
  84. data/spec/spec_tests/data/cmap/pool-checkin.yml +1 -0
  85. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +2 -0
  86. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +5 -0
  87. data/spec/spec_tests/data/cmap/pool-checkout-multiple.yml +3 -0
  88. data/spec/spec_tests/data/cmap/pool-checkout-no-idle.yml +4 -0
  89. data/spec/spec_tests/data/cmap/pool-checkout-no-stale.yml +4 -0
  90. data/spec/spec_tests/data/cmap/pool-close-destroy-conns.yml +2 -0
  91. data/spec/spec_tests/data/cmap/pool-create-max-size.yml +15 -0
  92. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +4 -0
  93. data/spec/spec_tests/data/cmap/wait-queue-fairness.yml +31 -1
  94. data/spec/spec_tests/data/cmap/wait-queue-timeout.yml +5 -0
  95. data/spec/spec_tests/data/read_write_concern/connection-string/read-concern.yml +32 -0
  96. data/spec/spec_tests/data/read_write_concern/connection-string/write-concern.yml +82 -0
  97. data/spec/spec_tests/data/read_write_concern/document/read-concern.yml +37 -0
  98. data/spec/spec_tests/data/read_write_concern/document/write-concern.yml +100 -0
  99. data/spec/spec_tests/data/retryable_reads/aggregate-merge.yml +39 -0
  100. data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +1 -1
  101. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +2 -2
  102. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +1 -1
  103. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +2 -2
  104. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +1 -1
  105. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +2 -2
  106. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +1 -1
  107. data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +1 -1
  108. data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +1 -1
  109. data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +1 -1
  110. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors.yml +1 -1
  111. data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +1 -1
  112. data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +1 -1
  113. data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +1 -1
  114. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +1 -1
  115. data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +1 -1
  116. data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +1 -1
  117. data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +1 -1
  118. data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +1 -1
  119. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +1 -1
  120. data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +1 -1
  121. data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +1 -1
  122. data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +1 -1
  123. data/spec/spec_tests/data/transactions/read-concern.yml +6 -6
  124. data/spec/spec_tests/data/transactions/transaction-options-repl.yml +117 -0
  125. data/spec/spec_tests/data/transactions/transaction-options.yml +14 -121
  126. data/spec/spec_tests/data/transactions/write-concern.yml +3 -0
  127. data/spec/spec_tests/data/transactions_api/transaction-options.yml +11 -12
  128. data/spec/spec_tests/dns_seedlist_discovery_spec.rb +17 -7
  129. data/spec/spec_tests/read_write_concern_connection_string_spec.rb +8 -0
  130. data/spec/spec_tests/read_write_concern_document_spec.rb +74 -0
  131. data/spec/spec_tests/retryable_reads_spec.rb +32 -1
  132. data/spec/spec_tests/uri_options_spec.rb +4 -2
  133. data/spec/support/auth.rb +5 -14
  134. data/spec/support/certificates/client-x509.crt +78 -0
  135. data/spec/support/certificates/client-x509.key +27 -0
  136. data/spec/support/certificates/client-x509.pem +105 -0
  137. data/spec/support/change_streams.rb +8 -11
  138. data/spec/support/client_registry.rb +26 -12
  139. data/spec/support/cluster_tools.rb +2 -2
  140. data/spec/support/cmap.rb +11 -7
  141. data/spec/support/command_monitoring.rb +8 -8
  142. data/spec/support/connection_string.rb +56 -28
  143. data/spec/support/constraints.rb +8 -0
  144. data/spec/support/crud/spec.rb +5 -8
  145. data/spec/support/event_subscriber.rb +7 -0
  146. data/spec/support/gridfs.rb +4 -7
  147. data/spec/support/server_discovery_and_monitoring.rb +3 -8
  148. data/spec/support/server_selection.rb +4 -9
  149. data/spec/support/server_selection_rtt.rb +4 -7
  150. data/spec/support/spec_config.rb +47 -19
  151. data/spec/support/spec_setup.rb +5 -0
  152. data/spec/support/utils.rb +46 -8
  153. metadata +637 -597
  154. metadata.gz.sig +0 -0
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.11.0.rc0'.freeze
20
+ VERSION = '2.11.0'.freeze
21
21
  end
@@ -9,11 +9,20 @@ Gem::Specification.new do |s|
9
9
 
10
10
  s.authors = ['Tyler Brock', 'Emily Stolfo', 'Durran Jordan']
11
11
  s.email = 'mongodb-dev@googlegroups.com'
12
- s.homepage = 'http://www.mongodb.org'
12
+ s.homepage = 'https://docs.mongodb.com/ruby-driver/'
13
13
  s.summary = 'Ruby driver for MongoDB'
14
14
  s.description = 'A Ruby driver for MongoDB'
15
15
  s.license = 'Apache-2.0'
16
16
 
17
+ s.metadata = {
18
+ 'bug_tracker_uri' => 'https://jira.mongodb.org/projects/RUBY',
19
+ 'changelog_uri' => 'https://github.com/mongodb/mongo-ruby-driver/releases',
20
+ 'documentation_uri' => 'https://docs.mongodb.com/ruby-driver/',
21
+ 'homepage_uri' => 'https://docs.mongodb.com/ruby-driver/',
22
+ 'mailing_list_uri' => 'https://groups.google.com/group/mongodb-user',
23
+ 'source_code_uri' => 'https://github.com/mongodb/mongo-ruby-driver',
24
+ }
25
+
17
26
  if File.exists?('gem-private_key.pem')
18
27
  s.signing_key = 'gem-private_key.pem'
19
28
  s.cert_chain = ['gem-public_cert.pem']
@@ -31,5 +40,5 @@ Gem::Specification.new do |s|
31
40
 
32
41
  s.required_ruby_version = ">= 2.3"
33
42
 
34
- s.add_dependency 'bson', '>=4.4.2', '<5.0.0'
43
+ s.add_dependency 'bson', '>=4.6.0', '<5.0.0'
35
44
  end
@@ -42,7 +42,7 @@ launched as follows:
42
42
  # Launch mongod in one terminal
43
43
  mkdir /tmp/mdb
44
44
  mongod --dbpath /tmp/mdb
45
-
45
+
46
46
  # Run tests in another terminal
47
47
  rake
48
48
 
@@ -63,7 +63,7 @@ First, install [mtools](https://github.com/rueckstiess/mtools):
63
63
  export PATH=~/.local/bin:$PATH
64
64
  # On MacOS:
65
65
  export PATH=$PATH:~/Library/Python/2.7/bin
66
-
66
+
67
67
  Then, launch a replica set:
68
68
 
69
69
  mlaunch init --replicaset --name ruby-driver-rs \
@@ -89,6 +89,28 @@ other tests require a sharded cluster with more than one shard. Tests requiring
89
89
  a single shard can be run against a deployment with multiple shards by
90
90
  specifying only one mongos address in MONGODB_URI.
91
91
 
92
+ ## Note Regarding SSL/TLS Arguments
93
+
94
+ MongoDB 4.2 (server and shell) added new command line options for setting TLS
95
+ parameters. These options follow the naming of URI options used by both the
96
+ shell and MongoDB drivers starting with MongoDB 4.2. The new options start with
97
+ the `--tls` prefix.
98
+
99
+ Old options, starting with the `--ssl` prefix, are still supported for backwards
100
+ compatibility, but their use is deprecated. As of this writing, mlaunch only
101
+ supports the old `--ssl` prefix options.
102
+
103
+ In the rest of this document, when TLS options are given for `mongo` or
104
+ `mongod` they use the new `--tls` prefixed arguments, and when the same options
105
+ are given to `mlaunch` they use the old `--ssl` prefixed forms. The conversion
106
+ table of the options used herein is as follows:
107
+
108
+ | --tls prefixed option | --ssl prefixed option |
109
+ | ----------------------- | --------------------- |
110
+ | --tls | --ssl |
111
+ | --tlsCAFile | --sslCAFile |
112
+ | --tlsCertificateKeyFile | --sslPEMKeyFile |
113
+
92
114
  ## TLS With Verification
93
115
 
94
116
  The test suite includes a set of TLS certificates for configuring a server
@@ -114,13 +136,13 @@ The driver's test suite is configured to verify certificates by default.
114
136
  If the server is launched with the certificates from the driver's test suite,
115
137
  the test suite can be run simply by specifying `tls=true` URI option:
116
138
 
117
- MONGODB_URI='mongodb://localhost:27017/?tls=true' rake
139
+ MONGODB_URI='mongodb://localhost:27017/?tls=true' rake
118
140
 
119
141
  The driver's test suite can also be executed against a server launched with
120
142
  any other certificates. In this case the certificates need to be explicitly
121
143
  specified in the URI, for example as follows:
122
144
 
123
- MONGODB_URI='mongodb://localhost:27017/?tls=true&tlsCAFile=path/to/ca.crt&tlsCertificateKeyFile=path/to/client.pem' rake
145
+ MONGODB_URI='mongodb://localhost:27017/?tls=true&tlsCAFile=path/to/ca.crt&tlsCertificateKeyFile=path/to/client.pem' rake
124
146
 
125
147
  Note that some tests (specifically testing TLS verification) expect the server
126
148
  to be launched using the certificates in the driver's test suite, and will
@@ -140,7 +162,7 @@ case a standalone server can be started as follows:
140
162
  To run the test suite against such a server, also omitting certificate
141
163
  verification, run:
142
164
 
143
- MONGODB_URI='mongodb://localhost:27017/?tls=true&tlsInsecure=true' rake
165
+ MONGODB_URI='mongodb://localhost:27017/?tls=true&tlsInsecure=true' rake
144
166
 
145
167
  Note that there are tests in the test suite that cover TLS verification, and
146
168
  they may fail if the test suite is run in this way.
@@ -153,7 +175,81 @@ mlaunch can configure authentication on the server:
153
175
 
154
176
  To run the test suite against such a server, run:
155
177
 
156
- MONGODB_URI='mongodb://dev:dev@localhost:27017/' rake
178
+ MONGODB_URI='mongodb://dev:dev@localhost:27017/' rake
179
+
180
+ ## X.509 Authentication
181
+
182
+ Note: Testing X.509 authentication requires an enterprise build of the MongoDB
183
+ server.
184
+
185
+ To set up a server configured for authentication with an X.509 certificate,
186
+ first launch a TLS-enabled server with a regular credentialed user.
187
+
188
+ The credentialed user is required because mlaunch configures `--keyFile`
189
+ option for cluster member authentication, which in turn enables authentication.
190
+ With authentication enabled, `mongod` allows creating the first user in the
191
+ `admin` database but the X.509 user must be created in the `$external`
192
+ database - as a result, the X.509 user cannot be the only user in the deployment.
193
+
194
+ Run the following command to set up a standalone `mongod` with a bootstrap
195
+ user:
196
+
197
+ mlaunch init --single --dir /tmp/mdb-x509 --sslMode requireSSL \
198
+ --sslPEMKeyFile `pwd`/spec/support/certificates/server.pem \
199
+ --sslCAFile `pwd`/spec/support/certificates/ca.crt \
200
+ --sslClientCertificate `pwd`/spec/support/certificates/client.pem \
201
+ --auth --username bootstrap --password bootstrap
202
+
203
+ Next, create the X.509 user. The command to create the user is the same
204
+ across all supported MongoDB versions, and for convenience we assign its text
205
+ to a variable as follows:
206
+
207
+ create_user_cmd="`cat <<'EOT'
208
+ db.getSiblingDB("$external").runCommand(
209
+ {
210
+ createUser: "C=US,ST=New York,L=New York City,O=MongoDB,OU=x509,CN=localhost",
211
+ roles: [
212
+ { role: "dbAdminAnyDatabase", db: "admin" },
213
+ { role: "readWriteAnyDatabase", db: "admin" },
214
+ { role: "userAdminAnyDatabase", db: "admin" },
215
+ { role: "clusterAdmin", db: "admin" },
216
+ ],
217
+ writeConcern: { w: "majority" , wtimeout: 5000 },
218
+ }
219
+ )
220
+ EOT
221
+ `"
222
+
223
+ Use the MongoDB shell to execute this command:
224
+
225
+ mongo --tls \
226
+ --tlsCAFile `pwd`/spec/support/certificates/ca.crt \
227
+ --tlsCertificateKeyFile `pwd`/spec/support/certificates/client-x509.pem \
228
+ -u bootstrap -p bootstrap \
229
+ --eval "$create_user_cmd"
230
+
231
+ Verify that authentication is required by running the following command, which
232
+ should fail:
233
+
234
+ mongo --tls \
235
+ --tlsCAFile `pwd`/spec/support/certificates/ca.crt \
236
+ --tlsCertificateKeyFile `pwd`/spec/support/certificates/client-x509.pem \
237
+ --eval 'db.serverStatus()'
238
+
239
+ Verify that X.509 authentication works by running the following command:
240
+
241
+ mongo --tls \
242
+ --tlsCAFile `pwd`/spec/support/certificates/ca.crt \
243
+ --tlsCertificateKeyFile `pwd`/spec/support/certificates/client-x509.pem \
244
+ --authenticationDatabase '$external' \
245
+ --authenticationMechanism MONGODB-X509 \
246
+ --eval 'db.serverStatus()'
247
+
248
+ The test suite includes a set of integration tests for X.509 client authentication.
249
+
250
+ To run the test suite against such a server, run:
251
+
252
+ MONGODB_URI="mongodb://localhost:27017/?authMechanism=MONGODB-X509&tls=true&tlsCAFile=spec/support/certificates/ca.crt&tlsCertificateKeyFile=spec/support/certificates/client-x509.pem" rake
157
253
 
158
254
  ## Compression
159
255
 
@@ -165,9 +261,9 @@ Generally, all URI options recognized by the driver may be set for a test run,
165
261
  and will cause the clients created by the test suite to have those options
166
262
  by default. For example, retryable writes may be turned on and off as follows:
167
263
 
168
- MONGODB_URI='mongodb://localhost:27017/?retryWrites=true' rake
264
+ MONGODB_URI='mongodb://localhost:27017/?retryWrites=true' rake
169
265
 
170
- MONGODB_URI='mongodb://localhost:27017/?retryWrites=false' rake
266
+ MONGODB_URI='mongodb://localhost:27017/?retryWrites=false' rake
171
267
 
172
268
  Individual tests may override options that the test suite uses as defaults.
173
269
  For example, retryable writes tests may create clients with the retry writes
@@ -177,7 +273,7 @@ the entire test run.
177
273
  It is also possible to, for example, reference non-default hosts and replica
178
274
  set names:
179
275
 
180
- MONGODB_URI='mongodb://test.host:27017,test.host:27018/?replicaSet=fooset' rake
276
+ MONGODB_URI='mongodb://test.host:27017,test.host:27018/?replicaSet=fooset' rake
181
277
 
182
278
  However, as noted in the caveats section, changing the database name used by
183
279
  the test suite is not supported.
@@ -0,0 +1,72 @@
1
+ # Test Users
2
+
3
+ The Mongo Ruby Driver tests assume the presence of two `Mongo::Auth::User` objects:
4
+ `root_user` and `test_user`. This document details the roles and privileges granted
5
+ to those users as well as how they are created and used in the tests.
6
+
7
+ Both users are defined in the [spec_config](support/spec_config.rb#L376) file.
8
+
9
+ ## root_user
10
+ `root_user` is the test user with the most privileges. It is created with the following roles:
11
+ - userAdminAnyDatabase
12
+ - dbAdminAnyDatabase
13
+ - readWriteAnyDatabase
14
+ - clusterAdmin
15
+
16
+ By default, `root_user` is given a username of `root-user` and a password of `password`.
17
+ However, you may override these defaults by specifying a username and password in the
18
+ `MONGODB_URI` environment variable while running your tests. For example, if you set `MONGODB_URI` to: `mongodb://alanturing:enigma@localhost:27017/`, the username of `root_user` would be set to `alanturing`, and the password would be set to `enigma`.
19
+
20
+ ## test_user
21
+ `test_user` is the user created with a more limited set of privileges. It is created with the following
22
+ roles:
23
+ - readWrite on the ruby-driver database
24
+ - dbAdmin on the ruby-driver database
25
+
26
+ It is also granted the following roles against a database called "invalid_database." These permissions are used for the purpose of running tests against a database that doesn't exist.
27
+ - readWrite on the invalid_database database
28
+ - dbAdmin on the invalid_database database
29
+
30
+ `test_user` also has the following roles, which are exclusively used to test transactions:
31
+ - readWrite on the hr database
32
+ - dbAdmin on the hr database
33
+ - readWrite on the reporting database
34
+ - dbAdmin on the reporting database
35
+
36
+ The `test_user` has the username `test-user` and the password `password`; these values are not customizable without changing the source code.
37
+
38
+ ## User Creation
39
+
40
+ Both users are typically created in the [spec_setup](support/spec_setup.rb) script, which can be
41
+ run in two ways: either by running `bundle exec rake spec:prepare`, which only runs spec setup without
42
+ running any actual tests, or by running `rake`, which runs spec setup and the entire test suite.
43
+
44
+ First, the `spec_setup` script attempts to create the `root_user`. If this user already exists (for example,
45
+ if you have already created this user in your test instance), `spec_setup` will skip this step. Once
46
+ the script has verified the existence of `root_user`, it will create a client authenticated with the `root_user` and use that client to create a second user, `test_user`. Because `root_user` has the `userAdminAnyDatabase` role, it has the permissions necessary to create and destroy users on your MongoDB instance. If you have already created a user with the same credentials as `test_user` prior to running
47
+ the `spec_setup` script, the script will delete this user and re-create it.
48
+
49
+ The `root_user` is created in the `admin` database, while the `test_user` is created in the `ruby-driver`
50
+ database.
51
+
52
+ The authentication mechanism used to store the user credentials is going to change depending on the version of MongoDB running on your deployment. If you are running tests against a MongoDB instance with a server version older than 3.0, the users will be created using the `MONGODB-CR` authentication mechanism. If your server version is between 3.0 and 3.6 (inclusive), the test users will be created using the `SCRAM-SHA-1` mechanism, which was introduced as the new default starting in MongoDB version 3.0. If you are running a version of MongoDB newer than 4.0, test users will be authenticated using either `SCRAM-SHA-1` or `SCRAM-SHA-256`.
53
+
54
+ **Note:** (m-launch)[http://blog.rueckstiess.com/mtools/mlaunch.html], the client tool we use to spin up MongoDB instances for our tests, creates users EXCLUSIVELY with the `SCRAM-SHA-1` mechanism, even when `SCRAM-SHA-256` is enabled on the test server. This should not impact your ability to run the Mongo Ruby Driver test suite.
55
+
56
+ ## Test Usage
57
+
58
+ `root_user` is used in the Mongo Ruby Driver tests to perform functionality that requires its high-level
59
+ roles and privileges (if your client is set up with authentication), such as creating and destroying users and database administration. To easily set up a `Mongo::Client` object authenticated with the roles and privileges of `root_user`, you can initialize a client using the `ClientRegistry` module as follows:
60
+
61
+ ```
62
+ client = ClientRegistry.instance.global_client('root_authorized')
63
+ ```
64
+
65
+ Of course, not every test will require you to create a client with so many privileges. Often, it is enough
66
+ to have a user who is only authorized to read and write to a specific test database. In this case, it is preferable to use `test_user`. To initialize a `Mongo::Client` object authenticated with the `test_user` object, use the `ClientRegistry` module as follows:
67
+
68
+ ```
69
+ client = ClientRegistry.instance.global_client('authorized')
70
+ ```
71
+
72
+ Once you have initialized these client objects, you may use them to perform functionality required by your tests.
@@ -41,7 +41,7 @@ describe 'Auth' do
41
41
  it 'indicates scram-sha-1 was used' do
42
42
  expect do
43
43
  connection.connect!
44
- end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
44
+ end.to raise_error(Mongo::Auth::Unauthorized, /User nonexistent_user \(mechanism: scram\) is not authorized to access admin.*\(used mechanism: SCRAM-SHA-1\)/)
45
45
  end
46
46
  end
47
47
 
@@ -53,7 +53,7 @@ describe 'Auth' do
53
53
  it 'indicates scram-sha-1 was used' do
54
54
  expect do
55
55
  connection.connect!
56
- end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
56
+ end.to raise_error(Mongo::Auth::Unauthorized, /User nonexistent_user \(mechanism: scram\) is not authorized to access admin.*\(used mechanism: SCRAM-SHA-1\)/)
57
57
  end
58
58
  end
59
59
  end
@@ -73,7 +73,7 @@ describe 'Auth' do
73
73
  it 'indicates scram-sha-1 was used' do
74
74
  expect do
75
75
  connection.connect!
76
- end.to raise_error(Mongo::Auth::Unauthorized, "User existing_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)")
76
+ end.to raise_error(Mongo::Auth::Unauthorized, /User existing_user \(mechanism: scram\) is not authorized to access admin.*\(used mechanism: SCRAM-SHA-1\)/)
77
77
  end
78
78
  end
79
79
 
@@ -85,7 +85,7 @@ describe 'Auth' do
85
85
  it 'indicates scram-sha-256 was used' do
86
86
  expect do
87
87
  connection.connect!
88
- end.to raise_error(Mongo::Auth::Unauthorized, "User existing_user (mechanism: scram256) is not authorized to access admin (used mechanism: SCRAM-SHA-256)")
88
+ end.to raise_error(Mongo::Auth::Unauthorized, /User existing_user \(mechanism: scram256\) is not authorized to access admin.*\(used mechanism: SCRAM-SHA-256\)/)
89
89
  end
90
90
  end
91
91
  end
@@ -101,7 +101,7 @@ describe 'Auth' do
101
101
  it 'indicates scram-sha-1 was requested and used' do
102
102
  expect do
103
103
  connection.connect!
104
- end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
104
+ end.to raise_error(Mongo::Auth::Unauthorized, /User nonexistent_user \(mechanism: scram\) is not authorized to access admin.*\(used mechanism: SCRAM-SHA-1\)/)
105
105
  end
106
106
  end
107
107
 
@@ -114,13 +114,27 @@ describe 'Auth' do
114
114
  it 'indicates scram-sha-256 was requested and used' do
115
115
  expect do
116
116
  connection.connect!
117
- end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram256) is not authorized to access admin (used mechanism: SCRAM-SHA-256)')
117
+ end.to raise_error(Mongo::Auth::Unauthorized, /User nonexistent_user \(mechanism: scram256\) is not authorized to access admin.*\(used mechanism: SCRAM-SHA-256\)/)
118
118
  end
119
119
  end
120
120
  end
121
121
 
122
+ context 'when authentication fails' do
123
+ let(:options) { SpecConfig.instance.ssl_options.merge(
124
+ user: 'nonexistent_user', password: 'foo') }
125
+
126
+ it 'reports auth source used' do
127
+ expect do
128
+ connection.connect!
129
+ end.to raise_error(Mongo::Auth::Unauthorized, /User nonexistent_user.*is not authorized to access admin \(auth source: admin\)/)
130
+ end
131
+ end
132
+
122
133
  context 'attempting to connect to a non-tls server with tls' do
123
134
  require_no_tls
135
+ # The exception raised is SocketTimeout on 3.6 server for whatever reason,
136
+ # run the test on 4.0+ only.
137
+ min_server_fcv '4.0'
124
138
 
125
139
  let(:options) { {ssl: true} }
126
140
 
@@ -7,8 +7,10 @@ describe 'Client construction' do
7
7
  SpecConfig.instance.test_options.merge(
8
8
  server_selection_timeout: 5,
9
9
  database: SpecConfig.instance.test_db,
10
+ ).merge(SpecConfig.instance.credentials_or_x509(
10
11
  user: SpecConfig.instance.test_user.name,
11
- password: SpecConfig.instance.test_user.password)
12
+ password: SpecConfig.instance.test_user.password,
13
+ ))
12
14
  end
13
15
 
14
16
  context 'in single topology' do
@@ -0,0 +1,437 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Client options' do
4
+ let(:uri) { "mongodb://#{credentials}127.0.0.1:27017/#{options}" }
5
+
6
+ let(:credentials) { nil }
7
+ let(:options) { nil }
8
+
9
+ let(:client_opts) { {} }
10
+
11
+ let(:client) { new_local_client_nmio(uri, client_opts) }
12
+
13
+ let(:user) { 'username' }
14
+ let(:pwd) { 'password' }
15
+
16
+ shared_examples_for 'a supported auth mechanism' do
17
+ context 'with URI options' do
18
+ let(:credentials) { "#{user}:#{pwd}@" }
19
+ let(:options) { "?authMechanism=#{auth_mech_string}" }
20
+
21
+ it 'creates a client with the correct auth mechanism' do
22
+ expect(client.options[:auth_mech]).to eq(auth_mech_sym)
23
+ end
24
+ end
25
+
26
+ context 'with client options' do
27
+ let(:client_opts) do
28
+ {
29
+ auth_mech: auth_mech_sym,
30
+ user: user,
31
+ password: pwd,
32
+ }
33
+ end
34
+
35
+ it 'creates a client with the correct auth mechanism' do
36
+ expect(client.options[:auth_mech]).to eq(auth_mech_sym)
37
+ end
38
+ end
39
+ end
40
+
41
+ shared_examples_for 'auth mechanism that uses database or default auth source' do |default_auth_source:|
42
+ context 'where no database is provided' do
43
+ context 'with URI options' do
44
+ let(:credentials) { "#{user}:#{pwd}@" }
45
+ let(:options) { "?authMechanism=#{auth_mech_string}" }
46
+
47
+ it 'creates a client with default auth source' do
48
+ expect(client.options['auth_source']).to eq(default_auth_source)
49
+ end
50
+ end
51
+
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
65
+ end
66
+
67
+ context 'where database is provided' do
68
+ let(:database) { 'test-db' }
69
+
70
+ context 'with URI options' do
71
+ let(:credentials) { "#{user}:#{pwd}@" }
72
+ let(:options) { "#{database}?authMechanism=#{auth_mech_string}" }
73
+
74
+ it 'creates a client with database as auth source' do
75
+ expect(client.options['auth_source']).to eq(database)
76
+ end
77
+ end
78
+
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
93
+ end
94
+ end
95
+
96
+ shared_examples_for 'an auth mechanism with ssl' do
97
+ let(:ca_file_path) { '/path/to/ca.pem' }
98
+ let(:cert_path) { '/path/to/client.pem' }
99
+
100
+ context 'with URI options' do
101
+ let(:credentials) { "#{user}:#{pwd}@" }
102
+ let(:options) { "?authMechanism=#{auth_mech_string}&tls=true&tlsCAFile=#{ca_file_path}&tlsCertificateKeyFile=#{cert_path}" }
103
+
104
+ it 'creates a client with ssl properties' do
105
+ expect(client.options[:ssl]).to be true
106
+ expect(client.options[:ssl_cert]).to eq(cert_path)
107
+ expect(client.options[:ssl_ca_cert]).to eq(ca_file_path)
108
+ expect(client.options[:ssl_key]).to eq(cert_path)
109
+ end
110
+ end
111
+
112
+ context 'with client options' do
113
+ let(:client_opts) do
114
+ {
115
+ auth_mech: auth_mech_sym,
116
+ ssl: true,
117
+ ssl_cert: cert_path,
118
+ ssl_key: cert_path,
119
+ ssl_ca_cert: ca_file_path,
120
+ user: user,
121
+ password: pwd
122
+ }
123
+ end
124
+
125
+ it 'creates a client with ssl properties' do
126
+ expect(client.options[:ssl]).to be true
127
+ expect(client.options[:ssl_cert]).to eq(cert_path)
128
+ expect(client.options[:ssl_ca_cert]).to eq(ca_file_path)
129
+ expect(client.options[:ssl_key]).to eq(cert_path)
130
+ end
131
+ end
132
+ end
133
+
134
+ shared_examples_for 'an auth mechanism that doesn\'t support auth_mech_properties' do
135
+ context 'with URI options' do
136
+ let(:credentials) { "#{user}:#{pwd}@" }
137
+ let(:options) { "?authMechanism=#{auth_mech_string}&authMechanismProperties=CANONICALIZE_HOST_NAME:true" }
138
+
139
+ it 'raises an exception on client creation' do
140
+ expect {
141
+ client
142
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /mechanism_properties are not supported/)
143
+ end
144
+ end
145
+
146
+ context 'with client options' do
147
+ let(:client_opts) do
148
+ {
149
+ auth_mech: auth_mech_sym,
150
+ user: user,
151
+ password: pwd,
152
+ auth_mech_properties: {
153
+ canonicalize_host_name: true
154
+ }
155
+ }
156
+ end
157
+
158
+ it 'raises an exception on client creation' do
159
+ expect {
160
+ client
161
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /mechanism_properties are not supported/)
162
+ end
163
+ end
164
+ end
165
+
166
+ shared_examples_for 'an auth mechanism that doesn\'t support invalid auth sources' do
167
+ context 'with URI options' do
168
+ let(:credentials) { "#{user}:#{pwd}@" }
169
+ let(:options) { "?authMechanism=#{auth_mech_string}&authSource=foo" }
170
+
171
+ it 'raises an exception on client creation' do
172
+ expect {
173
+ client
174
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /invalid auth source/)
175
+ end
176
+ end
177
+
178
+ context 'with client options' do
179
+ let(:client_opts) do
180
+ {
181
+ auth_mech: auth_mech_sym,
182
+ user: user,
183
+ password: pwd,
184
+ auth_source: 'foo'
185
+ }
186
+ end
187
+
188
+ it 'raises an exception on client creation' do
189
+ expect {
190
+ client
191
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /invalid auth source/)
192
+ end
193
+ end
194
+ end
195
+
196
+ context 'with MONGODB-CR auth mechanism' do
197
+ let(:auth_mech_string) { 'MONGODB-CR' }
198
+ let(:auth_mech_sym) { :mongodb_cr }
199
+
200
+ it_behaves_like 'a supported auth mechanism'
201
+ it_behaves_like 'auth mechanism that uses database or default auth source', default_auth_source: 'admin'
202
+ it_behaves_like 'an auth mechanism that doesn\'t support auth_mech_properties'
203
+ end
204
+
205
+ context 'with SCRAM-SHA-1 auth mechanism' do
206
+ let(:auth_mech_string) { 'SCRAM-SHA-1' }
207
+ let(:auth_mech_sym) { :scram }
208
+
209
+ it_behaves_like 'a supported auth mechanism'
210
+ it_behaves_like 'auth mechanism that uses database or default auth source', default_auth_source: 'admin'
211
+ it_behaves_like 'an auth mechanism that doesn\'t support auth_mech_properties'
212
+ end
213
+
214
+ context 'with SCRAM-SHA-256 auth mechanism' do
215
+ let(:auth_mech_string) { 'SCRAM-SHA-256' }
216
+ let(:auth_mech_sym) { :scram256 }
217
+
218
+ it_behaves_like 'a supported auth mechanism'
219
+ it_behaves_like 'auth mechanism that uses database or default auth source', default_auth_source: 'admin'
220
+ it_behaves_like 'an auth mechanism that doesn\'t support auth_mech_properties'
221
+ end
222
+
223
+ context 'with GSSAPI auth mechanism' do
224
+ require_mongo_kerberos
225
+
226
+ let(:auth_mech_string) { 'GSSAPI' }
227
+ let(:auth_mech_sym) { :gssapi }
228
+
229
+ it_behaves_like 'a supported auth mechanism'
230
+ it_behaves_like 'an auth mechanism that doesn\'t support invalid auth sources'
231
+
232
+ let(:auth_mech_properties) { { canonicalize_host_name: true, service_name: 'other'} }
233
+
234
+ context 'with URI options' do
235
+ let(:credentials) { "#{user}:#{pwd}@" }
236
+
237
+ context 'with default auth mech properties' do
238
+ let(:options) { '?authMechanism=GSSAPI' }
239
+
240
+ it 'correctly sets client options' do
241
+ expect(client.options[:auth_mech_properties]).to eq({ 'service_name' => 'mongodb' })
242
+ end
243
+ end
244
+ end
245
+
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
259
+ end
260
+
261
+ context 'with PLAIN auth mechanism' do
262
+ let(:auth_mech_string) { 'PLAIN' }
263
+ let(:auth_mech_sym) { :plain }
264
+
265
+ it_behaves_like 'a supported auth mechanism'
266
+ it_behaves_like 'auth mechanism that uses database or default auth source', default_auth_source: '$external'
267
+ it_behaves_like 'an auth mechanism with ssl'
268
+ it_behaves_like 'an auth mechanism that doesn\'t support auth_mech_properties'
269
+ end
270
+
271
+ context 'with MONGODB-X509 auth mechanism' do
272
+ let(:auth_mech_string) { 'MONGODB-X509' }
273
+ let(:auth_mech_sym) { :mongodb_x509 }
274
+
275
+ let(:pwd) { nil }
276
+
277
+ it_behaves_like 'a supported auth mechanism'
278
+ it_behaves_like 'an auth mechanism with ssl'
279
+ it_behaves_like 'an auth mechanism that doesn\'t support auth_mech_properties'
280
+ it_behaves_like 'an auth mechanism that doesn\'t support invalid auth sources'
281
+
282
+ context 'with URI options' do
283
+ let(:credentials) { "#{user}@" }
284
+ let(:options) { '?authMechanism=MONGODB-X509' }
285
+
286
+ it 'sets default auth source' do
287
+ expect(client.options[:auth_source]).to eq('$external')
288
+ end
289
+
290
+ context 'when username is not provided' do
291
+ let(:credentials) { '' }
292
+
293
+ it 'recognizes the mechanism with no username' do
294
+ expect(client.options[:user]).to be_nil
295
+ end
296
+ end
297
+
298
+ context 'when a password is provided' do
299
+ let(:credentials) { "#{user}:password@" }
300
+
301
+ it 'raises an exception on client creation' do
302
+ expect {
303
+ client
304
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /password is not supported/)
305
+ end
306
+ end
307
+ end
308
+
309
+ context 'with client options' do
310
+ let(:client_opts) { { auth_mech: :mongodb_x509, user: user } }
311
+
312
+ it 'sets default auth source' do
313
+ expect(client.options[:auth_source]).to eq('$external')
314
+ end
315
+
316
+ context 'when username is not provided' do
317
+ let(:client_opts) { { auth_mech: :mongodb_x509} }
318
+
319
+ it 'recognizes the mechanism with no username' do
320
+ expect(client.options[:user]).to be_nil
321
+ end
322
+ end
323
+
324
+ context 'when a password is provided' do
325
+ let(:client_opts) { { auth_mech: :mongodb_x509, user: user, password: 'password' } }
326
+
327
+ it 'raises an exception on client creation' do
328
+ expect {
329
+ client
330
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /password is not supported/)
331
+ end
332
+ end
333
+ end
334
+ end
335
+
336
+ context 'with no auth mechanism provided' do
337
+ context 'with URI options' do
338
+ context 'with no credentials' do
339
+ it 'creates a client without credentials' do
340
+ expect(client.options[:user]).to be_nil
341
+ expect(client.options[:password]).to be_nil
342
+ end
343
+ end
344
+
345
+ context 'with empty username' do
346
+ let(:credentials) { '@' }
347
+
348
+ it 'raises an exception' do
349
+ expect {
350
+ client
351
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /empty username is not supported/)
352
+ end
353
+ end
354
+ end
355
+
356
+ context 'with client options' do
357
+ context 'with no credentials' do
358
+ it 'creates a client without credentials' do
359
+ expect(client.options[:user]).to be_nil
360
+ expect(client.options[:password]).to be_nil
361
+ end
362
+ end
363
+
364
+ context 'with empty username' do
365
+ let(:client_opts) { { user: '', password: '' } }
366
+
367
+ it 'raises an exception' do
368
+ expect {
369
+ client
370
+ }.to raise_error(Mongo::Auth::InvalidConfiguration, /empty username is not supported/)
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ context 'with auth source provided' do
377
+ let(:auth_source) { 'foo' }
378
+
379
+ context 'with URI options' do
380
+ let(:options) { "?authSource=#{auth_source}" }
381
+
382
+ it 'correctly sets auth source on the client' do
383
+ expect(client.options[:auth_source]).to eq(auth_source)
384
+ end
385
+ end
386
+
387
+ context 'with client options' do
388
+ let(:client_opts) { { auth_source: auth_source } }
389
+
390
+ it 'correctly sets auth source on the client' do
391
+ expect(client.options[:auth_source]).to eq(auth_source)
392
+ end
393
+ end
394
+ end
395
+
396
+ context 'with auth mechanism properties' do
397
+ let(:service_name) { 'service name' }
398
+ let(:canonicalize_host_name) { true }
399
+ let(:service_realm) { 'service_realm' }
400
+
401
+ let(:auth_mechanism_properties) do
402
+ {
403
+ service_name: service_name,
404
+ canonicalize_host_name: canonicalize_host_name,
405
+ service_realm: service_realm
406
+ }
407
+ end
408
+
409
+ context 'with URI options' do
410
+ let(:options) do
411
+ "?authMechanismProperties=SERVICE_NAME:#{service_name}," +
412
+ "CANONICALIZE_HOST_NAME:#{canonicalize_host_name}," +
413
+ "SERVICE_REALM:#{service_realm}"
414
+ end
415
+
416
+ it 'correctly sets auth mechanism properties on the client' do
417
+ expect(client.options[:auth_mech_properties]).to eq({
418
+ 'service_name' => service_name,
419
+ 'canonicalize_host_name' => canonicalize_host_name,
420
+ 'service_realm' => service_realm
421
+ })
422
+ end
423
+ end
424
+
425
+ context 'with client options' do
426
+ let(:client_opts) { { auth_mech_properties: auth_mechanism_properties } }
427
+
428
+ it 'correctly sets auth mechanism properties on the client' do
429
+ expect(client.options[:auth_mech_properties]).to eq({
430
+ 'service_name' => service_name,
431
+ 'canonicalize_host_name' => canonicalize_host_name,
432
+ 'service_realm' => service_realm
433
+ })
434
+ end
435
+ end
436
+ end
437
+ end