mongo 2.11.1 → 2.11.6

Sign up to get free protection for your applications and to get access to all the features.
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