mongo 2.11.1 → 2.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -1
  3. data.tar.gz.sig +2 -1
  4. data/Rakefile +24 -0
  5. data/lib/mongo/address.rb +53 -37
  6. data/lib/mongo/auth.rb +30 -10
  7. data/lib/mongo/auth/cr.rb +1 -0
  8. data/lib/mongo/auth/cr/conversation.rb +13 -13
  9. data/lib/mongo/auth/ldap.rb +2 -1
  10. data/lib/mongo/auth/ldap/conversation.rb +9 -12
  11. data/lib/mongo/auth/scram.rb +1 -0
  12. data/lib/mongo/auth/scram/conversation.rb +36 -27
  13. data/lib/mongo/auth/user.rb +7 -1
  14. data/lib/mongo/auth/x509.rb +2 -1
  15. data/lib/mongo/auth/x509/conversation.rb +9 -9
  16. data/lib/mongo/bulk_write/transformable.rb +3 -3
  17. data/lib/mongo/client.rb +17 -6
  18. data/lib/mongo/cluster.rb +67 -49
  19. data/lib/mongo/cluster/sdam_flow.rb +87 -3
  20. data/lib/mongo/collection/view/readable.rb +3 -1
  21. data/lib/mongo/collection/view/writable.rb +3 -3
  22. data/lib/mongo/cursor/builder/kill_cursors_command.rb +8 -1
  23. data/lib/mongo/cursor/builder/op_kill_cursors.rb +8 -1
  24. data/lib/mongo/database.rb +1 -1
  25. data/lib/mongo/grid/file.rb +5 -0
  26. data/lib/mongo/grid/file/chunk.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +15 -13
  28. data/lib/mongo/grid/stream/write.rb +9 -3
  29. data/lib/mongo/protocol/serializers.rb +12 -2
  30. data/lib/mongo/server.rb +13 -6
  31. data/lib/mongo/server/connection.rb +15 -8
  32. data/lib/mongo/server/connection_base.rb +7 -4
  33. data/lib/mongo/server/description.rb +34 -21
  34. data/lib/mongo/server/monitor.rb +1 -1
  35. data/lib/mongo/server/monitor/connection.rb +2 -3
  36. data/lib/mongo/session.rb +10 -10
  37. data/lib/mongo/socket.rb +10 -1
  38. data/lib/mongo/uri.rb +1 -1
  39. data/lib/mongo/version.rb +1 -1
  40. data/mongo.gemspec +1 -1
  41. data/spec/README.md +13 -0
  42. data/spec/integration/auth_spec.rb +27 -8
  43. data/spec/integration/bson_symbol_spec.rb +34 -0
  44. data/spec/integration/client_construction_spec.rb +14 -0
  45. data/spec/integration/client_options_spec.rb +5 -5
  46. data/spec/integration/connection_spec.rb +57 -9
  47. data/spec/integration/crud_spec.rb +45 -0
  48. data/spec/integration/cursor_reaping_spec.rb +2 -1
  49. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  50. data/spec/integration/retryable_errors_spec.rb +2 -2
  51. data/spec/integration/zlib_compression_spec.rb +25 -0
  52. data/spec/lite_spec_helper.rb +1 -0
  53. data/spec/mongo/address_spec.rb +19 -13
  54. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  55. data/spec/mongo/auth/scram/conversation_spec.rb +25 -14
  56. data/spec/mongo/auth/user/view_spec.rb +39 -7
  57. data/spec/mongo/auth/user_spec.rb +12 -0
  58. data/spec/mongo/auth/x509/conversation_spec.rb +1 -1
  59. data/spec/mongo/bulk_write_spec.rb +2 -2
  60. data/spec/mongo/client_construction_spec.rb +3 -22
  61. data/spec/mongo/cluster_spec.rb +57 -0
  62. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  63. data/spec/mongo/collection_spec.rb +26 -2
  64. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +56 -0
  65. data/spec/mongo/server/connection_spec.rb +76 -8
  66. data/spec/mongo/server/monitor/connection_spec.rb +14 -7
  67. data/spec/mongo/socket/ssl_spec.rb +132 -98
  68. data/spec/mongo/socket/tcp_spec.rb +1 -9
  69. data/spec/mongo/uri_spec.rb +1 -1
  70. data/spec/runners/sdam/verifier.rb +6 -3
  71. data/spec/spec_tests/data/sdam/rs/primary_address_change.yml +29 -0
  72. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +27 -23
  73. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +56 -79
  74. data/spec/spec_tests/data/sdam/sharded/primary_address_change.yml +21 -0
  75. data/spec/spec_tests/data/sdam/sharded/primary_mismatched_me.yml +22 -0
  76. data/spec/spec_tests/data/sdam/single/primary_address_change.yml +24 -0
  77. data/spec/spec_tests/data/sdam/single/primary_mismatched_me.yml +25 -0
  78. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +159 -0
  79. data/spec/spec_tests/data/sdam_monitoring/{replica_set_other_seed.yml → replica_set_with_primary_change.yml} +97 -101
  80. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary_removal.yml +22 -18
  81. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +90 -0
  82. data/spec/support/cluster_config.rb +36 -0
  83. data/spec/support/cluster_tools.rb +5 -3
  84. data/spec/support/command_monitoring.rb +1 -1
  85. data/spec/support/constraints.rb +18 -18
  86. data/spec/support/lite_constraints.rb +8 -0
  87. data/spec/support/server_discovery_and_monitoring.rb +2 -0
  88. data/spec/support/spec_config.rb +3 -3
  89. data/spec/support/utils.rb +11 -1
  90. metadata +661 -637
  91. metadata.gz.sig +0 -0
@@ -575,7 +575,7 @@ describe Mongo::Collection::View::MapReduce do
575
575
  expect(map_reduce.send(:secondary_ok?)).to be false
576
576
  end
577
577
 
578
- context 'when the server is not a valid for writing' do
578
+ context 'when the server is not valid for writing' do
579
579
  clean_slate
580
580
 
581
581
  before do
@@ -278,9 +278,9 @@ describe Mongo::Collection do
278
278
  require_topology :replica_set
279
279
 
280
280
  let(:client_options) do
281
- {
281
+ SpecConfig.instance.auth_options.merge(
282
282
  read: { mode: :primary_preferred },
283
- }
283
+ )
284
284
  end
285
285
 
286
286
  let(:subscriber) { EventSubscriber.new }
@@ -945,6 +945,30 @@ describe Mongo::Collection do
945
945
  it_behaves_like 'a failed operation using a session'
946
946
  end
947
947
  end
948
+
949
+ context 'when collation has a strength' do
950
+ min_server_fcv '3.4'
951
+
952
+ let(:band_collection) do
953
+ described_class.new(database, :bands)
954
+ end
955
+
956
+ before do
957
+ band_collection.delete_many
958
+ band_collection.insert_many([{ name: "Depeche Mode" }, { name: "New Order" }])
959
+ end
960
+
961
+ let(:options) do
962
+ { collation: { locale: 'en_US', strength: 2 } }
963
+ end
964
+ let(:band_result) do
965
+ band_collection.find({ name: 'DEPECHE MODE' }, options)
966
+ end
967
+
968
+ it 'finds Capitalize from UPPER CASE' do
969
+ expect(band_result.count_documents).to eq(1)
970
+ end
971
+ end
948
972
  end
949
973
 
950
974
  describe '#drop' do
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Cursor::Builder::OpKillCursors do
4
+
5
+ let(:reply) do
6
+ Mongo::Protocol::Reply.allocate.tap do |reply|
7
+ allow(reply).to receive(:cursor_id).and_return(8000)
8
+ end
9
+ end
10
+
11
+ let(:result) do
12
+ Mongo::Operation::Result.new(reply)
13
+ end
14
+
15
+ let(:view) do
16
+ Mongo::Collection::View.new(
17
+ authorized_collection,
18
+ {},
19
+ tailable: true,
20
+ max_time_ms: 100
21
+ )
22
+ end
23
+
24
+ let(:cursor) do
25
+ Mongo::Cursor.new(view, result, authorized_primary)
26
+ end
27
+
28
+ let(:builder) do
29
+ described_class.new(cursor)
30
+ end
31
+
32
+ describe '#specification' do
33
+
34
+ let(:specification) do
35
+ builder.specification
36
+ end
37
+
38
+ it 'includes the cursor ids' do
39
+ expect(specification[:cursor_ids]).to eq([BSON::Int64.new(8000)])
40
+ end
41
+
42
+ it 'includes the database name' do
43
+ expect(specification[:db_name]).to eq(SpecConfig.instance.test_db)
44
+ end
45
+
46
+ it 'includes the collection name' do
47
+ expect(specification[:coll_name]).to eq(TEST_COLL)
48
+ end
49
+ end
50
+
51
+ describe '.get_cursors_list' do
52
+ it 'returns integer cursor ids' do
53
+ expect(described_class.get_cursors_list(builder.specification)).to eq([8000])
54
+ end
55
+ end
56
+ end
@@ -137,12 +137,13 @@ describe Mongo::Server::Connection, retry: 3 do
137
137
 
138
138
  it 'logs a warning' do
139
139
  messages = []
140
- # Straightforward expectations are not working here for some reason
141
140
  expect(Mongo::Logger.logger).to receive(:warn) do |msg|
142
141
  messages << msg
143
142
  end
143
+
144
144
  expect(error).not_to be nil
145
- expect(messages).to include(expected_message)
145
+
146
+ messages.any? { |msg| msg.include?(expected_message) }.should be true
146
147
  end
147
148
 
148
149
  end
@@ -419,6 +420,73 @@ describe Mongo::Server::Connection, retry: 3 do
419
420
  end
420
421
  end
421
422
 
423
+ context 'connecting to arbiter' do
424
+ require_topology :replica_set
425
+
426
+ before(:all) do
427
+ unless ENV['HAVE_ARBITER']
428
+ skip 'Test requires an arbiter in the deployment'
429
+ end
430
+ end
431
+
432
+ let(:arbiter_server) do
433
+ authorized_client.cluster.servers_list.each do |server|
434
+ server.scan!
435
+ end
436
+ server = authorized_client.cluster.servers_list.detect do |server|
437
+ server.arbiter?
438
+ end.tap do |server|
439
+ raise 'No arbiter in the deployment' unless server
440
+ end
441
+ end
442
+
443
+ shared_examples_for 'does not authenticate' do
444
+ let(:client) do
445
+ new_local_client([address],
446
+ SpecConfig.instance.test_options.merge(
447
+ :user => 'bogus',
448
+ :password => 'bogus',
449
+ :database => 'bogus'
450
+ ).merge(connect: :direct),
451
+ )
452
+ end
453
+
454
+ let(:connection) do
455
+ described_class.new(
456
+ server,
457
+ )
458
+ end
459
+
460
+ let(:ping) do
461
+ client.database.command(ping: 1)
462
+ end
463
+
464
+ it 'does not authenticate' do
465
+ ClientRegistry.instance.close_all_clients
466
+
467
+ expect_any_instance_of(Mongo::Server::Connection).not_to receive(:authenticate!)
468
+
469
+ expect(ping.documents.first['ok']).to eq(1) rescue nil
470
+ end
471
+ end
472
+
473
+ context 'without me mismatch' do
474
+ let(:address) do
475
+ arbiter_server.address.to_s
476
+ end
477
+
478
+ it_behaves_like 'does not authenticate'
479
+ end
480
+
481
+ context 'with me mismatch' do
482
+ let(:address) do
483
+ "#{ClusterConfig.instance.alternate_address.host}:#{arbiter_server.address.port}"
484
+ end
485
+
486
+ it_behaves_like 'does not authenticate'
487
+ end
488
+ end
489
+
422
490
  end
423
491
 
424
492
  describe '#disconnect!' do
@@ -1012,7 +1080,7 @@ describe Mongo::Server::Connection, retry: 3 do
1012
1080
  end
1013
1081
 
1014
1082
  it 'uses the connect_timeout for the address' do
1015
- expect(connection.address.send(:connect_timeout)).to eq(3)
1083
+ expect(connection.address.options[:connect_timeout]).to eq(3)
1016
1084
  end
1017
1085
 
1018
1086
  it 'uses the socket_timeout as the socket_timeout' do
@@ -1031,7 +1099,7 @@ describe Mongo::Server::Connection, retry: 3 do
1031
1099
  end
1032
1100
 
1033
1101
  it 'uses the connect_timeout for the address' do
1034
- expect(connection.address.send(:connect_timeout)).to eq(3)
1102
+ expect(connection.address.options[:connect_timeout]).to eq(3)
1035
1103
  end
1036
1104
 
1037
1105
  it 'does not use a socket_timeout' do
@@ -1052,8 +1120,8 @@ describe Mongo::Server::Connection, retry: 3 do
1052
1120
  connection.connect!
1053
1121
  end
1054
1122
 
1055
- it 'uses the default connect_timeout for the address' do
1056
- expect(connection.address.send(:connect_timeout)).to eq(10)
1123
+ it 'does not specify connect_timeout for the address' do
1124
+ expect(connection.address.options[:connect_timeout]).to be nil
1057
1125
  end
1058
1126
 
1059
1127
  it 'uses the socket_timeout' do
@@ -1071,8 +1139,8 @@ describe Mongo::Server::Connection, retry: 3 do
1071
1139
  connection.connect!
1072
1140
  end
1073
1141
 
1074
- it 'uses the default connect_timeout for the address' do
1075
- expect(connection.address.send(:connect_timeout)).to eq(10)
1142
+ it 'does not specify connect_timeout for the address' do
1143
+ expect(connection.address.options[:connect_timeout]).to be nil
1076
1144
  end
1077
1145
 
1078
1146
  it 'does not use a socket_timeout' do
@@ -66,7 +66,7 @@ describe Mongo::Server::Monitor::Connection do
66
66
  end
67
67
 
68
68
  it 'uses the connect_timeout for the address' do
69
- expect(connection.address.send(:connect_timeout)).to eq(3)
69
+ expect(connection.address.options[:connect_timeout]).to eq(3)
70
70
  end
71
71
 
72
72
  it 'uses the connect_timeout as the socket_timeout' do
@@ -81,7 +81,7 @@ describe Mongo::Server::Monitor::Connection do
81
81
  end
82
82
 
83
83
  it 'uses the connect_timeout for the address' do
84
- expect(connection.address.send(:connect_timeout)).to eq(3)
84
+ expect(connection.address.options[:connect_timeout]).to eq(3)
85
85
  end
86
86
 
87
87
  it 'uses the connect_timeout as the socket_timeout' do
@@ -98,8 +98,8 @@ describe Mongo::Server::Monitor::Connection do
98
98
  SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: 5)
99
99
  end
100
100
 
101
- it 'uses the default connect_timeout for the address' do
102
- expect(connection.address.send(:connect_timeout)).to eq(10)
101
+ it 'does not specify connect_timeout for the address' do
102
+ expect(connection.address.options[:connect_timeout]).to be nil
103
103
  end
104
104
 
105
105
  it 'uses the connect_timeout as the socket_timeout' do
@@ -113,8 +113,8 @@ describe Mongo::Server::Monitor::Connection do
113
113
  SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: nil)
114
114
  end
115
115
 
116
- it 'uses the default connect_timeout for the address' do
117
- expect(connection.address.send(:connect_timeout)).to eq(10)
116
+ it 'does not specify connect_timeout for the address' do
117
+ expect(connection.address.options[:connect_timeout]).to be nil
118
118
  end
119
119
 
120
120
  it 'uses the connect_timeout as the socket_timeout' do
@@ -189,10 +189,17 @@ describe Mongo::Server::Monitor::Connection do
189
189
 
190
190
  it 'logs a warning' do
191
191
  expect_any_instance_of(Mongo::Socket).to receive(:write).and_raise(Mongo::Error::SocketError, 'test error')
192
- expect(Mongo::Logger.logger).to receive(:warn).with(expected_message).and_call_original
192
+
193
+ messages = []
194
+ expect(Mongo::Logger.logger).to receive(:warn) do |msg|
195
+ messages << msg
196
+ end
197
+
193
198
  expect do
194
199
  monitor.connection.connect!
195
200
  end.to raise_error(Mongo::Error::SocketError, 'test error')
201
+
202
+ messages.any? { |msg| msg.include?(expected_message) }.should be true
196
203
  end
197
204
  end
198
205
  end
@@ -3,27 +3,18 @@ require 'spec_helper'
3
3
  # this test performs direct network connections without retries.
4
4
  # In case of intermittent network issues, retry the entire failing test.
5
5
  describe Mongo::Socket::SSL, retry: 3 do
6
+ clean_slate_for_all
6
7
  require_tls
7
8
 
8
- let(:address) do
9
- default_address.tap do
10
- ClientRegistry.instance.close_all_clients
11
- end
12
- end
13
-
14
- let!(:resolver) do
15
- address.send(:create_resolver, {})
16
- end
17
-
18
- let(:socket_timeout) do
19
- 1
20
- end
9
+ let(:host_name) { 'localhost' }
21
10
 
22
11
  let(:socket) do
23
- resolver.socket(socket_timeout, options)
12
+ described_class.new('127.0.0.1', default_address.port,
13
+ host_name, 1, :INET, ssl_options.merge(
14
+ connect_timeout: 2.4))
24
15
  end
25
16
 
26
- let(:options) do
17
+ let(:ssl_options) do
27
18
  SpecConfig.instance.ssl_options
28
19
  end
29
20
 
@@ -82,7 +73,7 @@ describe Mongo::Socket::SSL, retry: 3 do
82
73
 
83
74
  context 'when a certificate and key are provided as strings' do
84
75
 
85
- let(:options) do
76
+ let(:ssl_options) do
86
77
  {
87
78
  :ssl => true,
88
79
  :ssl_cert_string => cert_string,
@@ -99,7 +90,7 @@ describe Mongo::Socket::SSL, retry: 3 do
99
90
  context 'when certificate and an encrypted key are provided as strings' do
100
91
  require_local_tls
101
92
 
102
- let(:options) do
93
+ let(:ssl_options) do
103
94
  {
104
95
  :ssl => true,
105
96
  :ssl_cert_string => cert_string,
@@ -116,7 +107,7 @@ describe Mongo::Socket::SSL, retry: 3 do
116
107
 
117
108
  context 'when a certificate and key are provided as objects' do
118
109
 
119
- let(:options) do
110
+ let(:ssl_options) do
120
111
  {
121
112
  :ssl => true,
122
113
  :ssl_cert_object => cert_object,
@@ -132,7 +123,7 @@ describe Mongo::Socket::SSL, retry: 3 do
132
123
 
133
124
  context 'when the certificate is specified using both a file and a PEM-encoded string' do
134
125
 
135
- let(:options) do
126
+ let(:ssl_options) do
136
127
  super().merge(
137
128
  :ssl_cert_string => 'This is a random string, not a PEM-encoded certificate'
138
129
  )
@@ -146,7 +137,7 @@ describe Mongo::Socket::SSL, retry: 3 do
146
137
 
147
138
  context 'when the certificate is specified using both a file and an object' do
148
139
 
149
- let(:options) do
140
+ let(:ssl_options) do
150
141
  super().merge(
151
142
  :ssl_cert_object => 'This is a string, not a certificate'
152
143
  )
@@ -160,7 +151,7 @@ describe Mongo::Socket::SSL, retry: 3 do
160
151
 
161
152
  context 'when the certificate is specified using both a PEM-encoded string and an object' do
162
153
 
163
- let(:options) do
154
+ let(:ssl_options) do
164
155
  {
165
156
  :ssl => true,
166
157
  :ssl_cert_string => cert_string,
@@ -178,7 +169,7 @@ describe Mongo::Socket::SSL, retry: 3 do
178
169
 
179
170
  context 'when the key is specified using both a file and a PEM-encoded string' do
180
171
 
181
- let(:options) do
172
+ let(:ssl_options) do
182
173
  super().merge(
183
174
  :ssl_key_string => 'This is a normal string, not a PEM-encoded key'
184
175
  )
@@ -192,7 +183,7 @@ describe Mongo::Socket::SSL, retry: 3 do
192
183
 
193
184
  context 'when the key is specified using both a file and an object' do
194
185
 
195
- let(:options) do
186
+ let(:ssl_options) do
196
187
  super().merge(
197
188
  :ssl_cert_object => 'This is a string, not a key'
198
189
  )
@@ -206,7 +197,7 @@ describe Mongo::Socket::SSL, retry: 3 do
206
197
 
207
198
  context 'when the key is specified using both a PEM-encoded string and an object' do
208
199
 
209
- let(:options) do
200
+ let(:ssl_options) do
210
201
  {
211
202
  :ssl => true,
212
203
  :ssl_cert => SpecConfig.instance.client_cert_path,
@@ -224,7 +215,7 @@ describe Mongo::Socket::SSL, retry: 3 do
224
215
 
225
216
  context 'when a certificate is passed, but it is not of the right type' do
226
217
 
227
- let(:options) do
218
+ let(:ssl_options) do
228
219
  cert = "This is a string, not an X.509 Certificate"
229
220
  {
230
221
  :ssl => true,
@@ -247,38 +238,27 @@ describe Mongo::Socket::SSL, retry: 3 do
247
238
  end
248
239
 
249
240
  context 'when the hostname is verified' do
250
- it 'raises an error' do
251
- error = nil
252
- begin
253
- described_class.new(
254
- resolver.host,
255
- resolver.port,
256
- host_name,
257
- 30,
258
- ::Socket::PF_INET,
259
- options.merge(ssl_verify: false, ssl_verify_hostname: true)
260
- )
261
- rescue => e
262
- error = e
263
- end
264
241
 
265
- expect(error).to be_a(Mongo::Error::SocketError)
266
- expect(error.message).to eq('SSL handshake failed due to a hostname mismatch.')
242
+ let(:ssl_options) do
243
+ SpecConfig.instance.ssl_options.merge(ssl_verify: false, ssl_verify_hostname: true)
244
+ end
245
+
246
+ it 'raises an error' do
247
+ lambda do
248
+ socket
249
+ end.should raise_error(Mongo::Error::SocketError, /SSL handshake failed due to a hostname mismatch/)
267
250
  end
268
251
  end
269
252
 
270
253
  context 'when the hostname is not verified' do
271
- it 'raises an error' do
272
- expect {
273
- described_class.new(
274
- resolver.host,
275
- resolver.port,
276
- host_name,
277
- 30,
278
- ::Socket::PF_INET,
279
- options.merge(ssl_verify: false, ssl_verify_hostname: false)
280
- )
281
- }.not_to raise_error
254
+ let(:ssl_options) do
255
+ SpecConfig.instance.ssl_options.merge(ssl_verify: false, ssl_verify_hostname: false)
256
+ end
257
+
258
+ it 'does not raise an error' do
259
+ lambda do
260
+ socket
261
+ end.should_not raise_error
282
262
  end
283
263
  end
284
264
  end
@@ -288,7 +268,7 @@ describe Mongo::Socket::SSL, retry: 3 do
288
268
 
289
269
  context 'when a key is passed, but it is not of the right type' do
290
270
 
291
- let(:options) do
271
+ let(:ssl_options) do
292
272
  key = "This is a string not a key"
293
273
  {
294
274
  :ssl => true,
@@ -315,7 +295,7 @@ describe Mongo::Socket::SSL, retry: 3 do
315
295
 
316
296
  context 'when a key is passed, but it is not of the right type' do
317
297
 
318
- let(:options) do
298
+ let(:ssl_options) do
319
299
  key = "This is a string not a key"
320
300
  {
321
301
  :ssl => true,
@@ -347,35 +327,82 @@ describe Mongo::Socket::SSL, retry: 3 do
347
327
  end
348
328
  end
349
329
 
350
- context 'when a bad certificate is provided' do
330
+ context 'when a bad certificate/key is provided' do
351
331
 
352
- let(:options) do
353
- super().merge(
354
- :ssl_key => COMMAND_MONITORING_TESTS.first
355
- )
332
+ shared_examples_for 'raises an exception' do
333
+ it 'raises an exception' do
334
+ expect do
335
+ socket
336
+ end.to raise_exception(*expected_exception)
337
+ end
356
338
  end
357
339
 
358
- let(:expected_exception) do
359
- if SpecConfig.instance.jruby?
360
- # java.lang.ClassCastException: org.bouncycastle.asn1.DERApplicationSpecific cannot be cast to org.bouncycastle.asn1.ASN1Sequence
361
- # https://github.com/jruby/jruby-openssl/issues/171
362
- Exception
363
- else
364
- # mri
365
- if RUBY_VERSION >= '2.4.0'
366
- # OpenSSL::PKey::PKeyError: Could not parse PKey: no start line
367
- OpenSSL::OpenSSLError
368
- else
369
- # ArgumentError: Could not parse PKey: no start line
370
- ArgumentError
340
+ context 'mri' do
341
+ only_mri
342
+
343
+ context 'when a bad certificate is provided' do
344
+
345
+ let(:expected_exception) do
346
+ # OpenSSL::X509::CertificateError: nested asn1 error
347
+ [OpenSSL::OpenSSLError, /asn1 error/i]
348
+ end
349
+
350
+ let(:ssl_options) do
351
+ super().merge(
352
+ :ssl_cert => COMMAND_MONITORING_TESTS.first,
353
+ :ssl_key => nil,
354
+ )
355
+ end
356
+
357
+ it_behaves_like 'raises an exception'
358
+ end
359
+
360
+ context 'when a bad key is provided' do
361
+
362
+ let(:expected_exception) do
363
+ if RUBY_VERSION >= '2.4.0'
364
+ # OpenSSL::PKey::PKeyError: Could not parse PKey: no start line
365
+ [OpenSSL::OpenSSLError, /Could not parse PKey/]
366
+ else
367
+ # ArgumentError: Could not parse PKey: no start line
368
+ [ArgumentError, /Could not parse PKey/]
369
+ end
370
+ end
371
+
372
+ let(:ssl_options) do
373
+ super().merge(
374
+ :ssl_cert => nil,
375
+ :ssl_key => COMMAND_MONITORING_TESTS.first,
376
+ )
371
377
  end
378
+
379
+ it_behaves_like 'raises an exception'
372
380
  end
373
381
  end
374
382
 
375
- it 'raises an exception' do
376
- expect do
377
- socket
378
- end.to raise_exception(expected_exception)
383
+ context 'jruby' do
384
+ require_jruby
385
+
386
+ # On JRuby the key does not appear to be parsed, therefore only
387
+ # specifying the bad certificate produces an error.
388
+
389
+ context 'when a bad certificate is provided' do
390
+
391
+ let(:ssl_options) do
392
+ super().merge(
393
+ :ssl_cert => COMMAND_MONITORING_TESTS.first,
394
+ :ssl_key => nil,
395
+ )
396
+ end
397
+
398
+ let(:expected_exception) do
399
+ # java.lang.ClassCastException: org.bouncycastle.asn1.DERApplicationSpecific cannot be cast to org.bouncycastle.asn1.ASN1Sequence
400
+ # OpenSSL::X509::CertificateError: parsing issue: malformed PEM data: no header found
401
+ [OpenSSL::OpenSSLError, /malformed pem data/i]
402
+ end
403
+
404
+ it_behaves_like 'raises an exception'
405
+ end
379
406
  end
380
407
  end
381
408
 
@@ -384,7 +411,7 @@ describe Mongo::Socket::SSL, retry: 3 do
384
411
 
385
412
  context 'as a path to a file' do
386
413
 
387
- let(:options) do
414
+ let(:ssl_options) do
388
415
  super().merge(
389
416
  :ssl_ca_cert => SpecConfig.instance.local_ca_cert_path,
390
417
  :ssl_verify => true
@@ -398,7 +425,7 @@ describe Mongo::Socket::SSL, retry: 3 do
398
425
 
399
426
  context 'as a string containing the PEM-encoded certificate' do
400
427
 
401
- let(:options) do
428
+ let(:ssl_options) do
402
429
  super().merge(
403
430
  :ssl_ca_cert_string => ca_cert_string,
404
431
  :ssl_verify => true
@@ -411,7 +438,7 @@ describe Mongo::Socket::SSL, retry: 3 do
411
438
  end
412
439
 
413
440
  context 'as an array of Certificate objects' do
414
- let(:options) do
441
+ let(:ssl_options) do
415
442
  cert = [OpenSSL::X509::Certificate.new(ca_cert_string)]
416
443
  super().merge(
417
444
  :ssl_ca_cert_object => cert,
@@ -426,7 +453,7 @@ describe Mongo::Socket::SSL, retry: 3 do
426
453
 
427
454
  context 'both as a file and a PEM-encoded parameter' do
428
455
 
429
- let(:options) do
456
+ let(:ssl_options) do
430
457
  super().merge(
431
458
  :ssl_ca_cert => SpecConfig.instance.local_ca_cert_path,
432
459
  :ssl_ca_cert_string => 'This is a string, not a certificate',
@@ -442,7 +469,7 @@ describe Mongo::Socket::SSL, retry: 3 do
442
469
 
443
470
  context 'both as a file and as object parameter' do
444
471
 
445
- let(:options) do
472
+ let(:ssl_options) do
446
473
  super().merge(
447
474
  :ssl_ca_cert => SpecConfig.instance.local_ca_cert_path,
448
475
  :ssl_ca_cert_object => 'This is a string, not an array of certificates',
@@ -457,7 +484,7 @@ describe Mongo::Socket::SSL, retry: 3 do
457
484
 
458
485
  context 'both as a PEM-encoded string and as object parameter' do
459
486
 
460
- let(:options) do
487
+ let(:ssl_options) do
461
488
  cert = File.read(SpecConfig.instance.local_ca_cert_path)
462
489
  super().merge(
463
490
  :ssl_ca_cert_string => cert,
@@ -480,11 +507,11 @@ describe Mongo::Socket::SSL, retry: 3 do
480
507
  end
481
508
 
482
509
  let(:connection) do
483
- Mongo::Server::Connection.new(server, options.merge(socket_timeout: 2))
510
+ Mongo::Server::Connection.new(server, ssl_options.merge(socket_timeout: 2))
484
511
  end
485
512
 
486
513
  context 'as a file' do
487
- let(:options) do
514
+ let(:ssl_options) do
488
515
  SpecConfig.instance.test_options.merge(
489
516
  ssl: true,
490
517
  ssl_cert: SpecConfig.instance.client_cert_path,
@@ -511,11 +538,11 @@ describe Mongo::Socket::SSL, retry: 3 do
511
538
  end
512
539
 
513
540
  let(:connection) do
514
- Mongo::Server::Connection.new(server, options.merge(socket_timeout: 2))
541
+ Mongo::Server::Connection.new(server, ssl_options.merge(socket_timeout: 2))
515
542
  end
516
543
 
517
544
  context 'as a file' do
518
- let(:options) do
545
+ let(:ssl_options) do
519
546
  SpecConfig.instance.test_options.merge(
520
547
  ssl: true,
521
548
  ssl_cert: SpecConfig.instance.client_cert_path,
@@ -537,7 +564,7 @@ describe Mongo::Socket::SSL, retry: 3 do
537
564
  context 'when a CA certificate is not provided' do
538
565
  require_local_tls
539
566
 
540
- let(:options) do
567
+ let(:ssl_options) do
541
568
  super().merge(
542
569
  :ssl_verify => true
543
570
  )
@@ -566,12 +593,12 @@ describe Mongo::Socket::SSL, retry: 3 do
566
593
  end
567
594
 
568
595
  let(:connection) do
569
- Mongo::Server::Connection.new(server, options.merge(socket_timeout: 2))
596
+ Mongo::Server::Connection.new(server, ssl_options.merge(socket_timeout: 2))
570
597
  end
571
598
 
572
599
  context 'as a path to a file' do
573
600
  context 'standalone' do
574
- let(:options) do
601
+ let(:ssl_options) do
575
602
  SpecConfig.instance.test_options.merge(
576
603
  ssl_cert: SpecConfig.instance.second_level_cert_path,
577
604
  ssl_key: SpecConfig.instance.second_level_key_path,
@@ -581,6 +608,13 @@ describe Mongo::Socket::SSL, retry: 3 do
581
608
  end
582
609
 
583
610
  it 'fails' do
611
+ # This test provides a second level client certificate to the
612
+ # server *without* providing the intermediate certificate.
613
+ # If the server performs certificate verification, it will
614
+ # reject the connection (seen from the driver as a SocketError)
615
+ # and the test will succeed. If the server does not perform
616
+ # certificate verification, it will accept the connection,
617
+ # no SocketError will be raised and the test will fail.
584
618
  connection
585
619
  expect do
586
620
  connection.connect!
@@ -593,7 +627,7 @@ describe Mongo::Socket::SSL, retry: 3 do
593
627
  # https://github.com/jruby/jruby-openssl/issues/181
594
628
  only_mri
595
629
 
596
- let(:options) do
630
+ let(:ssl_options) do
597
631
  SpecConfig.instance.test_options.merge(
598
632
  ssl: true,
599
633
  ssl_cert: SpecConfig.instance.second_level_cert_bundle_path,
@@ -614,7 +648,7 @@ describe Mongo::Socket::SSL, retry: 3 do
614
648
 
615
649
  context 'as a string' do
616
650
  context 'standalone' do
617
- let(:options) do
651
+ let(:ssl_options) do
618
652
  SpecConfig.instance.test_options.merge(
619
653
  ssl_cert: nil,
620
654
  ssl_cert_string: File.read(SpecConfig.instance.second_level_cert_path),
@@ -638,7 +672,7 @@ describe Mongo::Socket::SSL, retry: 3 do
638
672
  # https://github.com/jruby/jruby-openssl/issues/181
639
673
  only_mri
640
674
 
641
- let(:options) do
675
+ let(:ssl_options) do
642
676
  SpecConfig.instance.test_options.merge(
643
677
  ssl: true,
644
678
  ssl_cert: nil,
@@ -668,11 +702,11 @@ describe Mongo::Socket::SSL, retry: 3 do
668
702
  end
669
703
 
670
704
  let(:connection) do
671
- Mongo::Server::Connection.new(server, options.merge(socket_timeout: 2))
705
+ Mongo::Server::Connection.new(server, ssl_options.merge(socket_timeout: 2))
672
706
  end
673
707
 
674
- let(:options) do
675
- SpecConfig.instance.test_options.merge(
708
+ let(:ssl_options) do
709
+ SpecConfig.instance.ssl_options.merge(
676
710
  ssl: true,
677
711
  ssl_cert: SpecConfig.instance.client_pem_path,
678
712
  ssl_key: SpecConfig.instance.client_pem_path,
@@ -692,7 +726,7 @@ describe Mongo::Socket::SSL, retry: 3 do
692
726
  context 'when ssl_verify is not specified' do
693
727
  require_local_tls
694
728
 
695
- let(:options) do
729
+ let(:ssl_options) do
696
730
  super().merge(
697
731
  :ssl_ca_cert => SpecConfig.instance.local_ca_cert_path
698
732
  ).tap { |options| options.delete(:ssl_verify) }
@@ -706,7 +740,7 @@ describe Mongo::Socket::SSL, retry: 3 do
706
740
  context 'when ssl_verify is true' do
707
741
  require_local_tls
708
742
 
709
- let(:options) do
743
+ let(:ssl_options) do
710
744
  super().merge(
711
745
  :ssl_ca_cert => SpecConfig.instance.local_ca_cert_path,
712
746
  :ssl_verify => true
@@ -720,7 +754,7 @@ describe Mongo::Socket::SSL, retry: 3 do
720
754
 
721
755
  context 'when ssl_verify is false' do
722
756
 
723
- let(:options) do
757
+ let(:ssl_options) do
724
758
  super().merge(
725
759
  :ssl_ca_cert => 'invalid',
726
760
  :ssl_verify => false