mongo 2.2.5 → 2.2.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 (41) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/auth/scram/conversation.rb +9 -3
  5. data/lib/mongo/error.rb +1 -0
  6. data/lib/mongo/error/unexpected_response.rb +38 -0
  7. data/lib/mongo/operation/commands/map_reduce/result.rb +1 -1
  8. data/lib/mongo/operation/commands/user_query.rb +1 -1
  9. data/lib/mongo/operation/commands/users_info/result.rb +6 -0
  10. data/lib/mongo/protocol/message.rb +10 -3
  11. data/lib/mongo/retryable.rb +23 -0
  12. data/lib/mongo/server/connectable.rb +7 -6
  13. data/lib/mongo/server/connection.rb +18 -2
  14. data/lib/mongo/server/connection_pool.rb +4 -6
  15. data/lib/mongo/server/monitor/connection.rb +6 -3
  16. data/lib/mongo/uri.rb +7 -8
  17. data/lib/mongo/version.rb +1 -1
  18. data/mongo.gemspec +1 -0
  19. data/spec/mongo/address_spec.rb +2 -2
  20. data/spec/mongo/auth/cr_spec.rb +1 -1
  21. data/spec/mongo/auth/ldap_spec.rb +1 -1
  22. data/spec/mongo/auth/scram_spec.rb +1 -1
  23. data/spec/mongo/auth/user/view_spec.rb +12 -0
  24. data/spec/mongo/auth/x509_spec.rb +1 -1
  25. data/spec/mongo/client_spec.rb +1 -9
  26. data/spec/mongo/operation/result_spec.rb +3 -3
  27. data/spec/mongo/operation/write/update_spec.rb +1 -1
  28. data/spec/mongo/server/connection_spec.rb +168 -4
  29. data/spec/mongo/server/monitor_spec.rb +34 -4
  30. data/spec/mongo/server_selector/nearest_spec.rb +4 -4
  31. data/spec/mongo/server_selector/primary_preferred_spec.rb +4 -4
  32. data/spec/mongo/server_selector/primary_spec.rb +2 -2
  33. data/spec/mongo/server_selector/secondary_preferred_spec.rb +4 -4
  34. data/spec/mongo/server_selector/secondary_spec.rb +3 -3
  35. data/spec/mongo/server_spec.rb +3 -3
  36. data/spec/mongo/socket/ssl_spec.rb +1 -1
  37. data/spec/mongo/uri_spec.rb +124 -23
  38. data/spec/support/authorization.rb +10 -6
  39. data/spec/support/shared/server_selector.rb +1 -1
  40. metadata +18 -3
  41. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 091537e3e22eeeced33e7b6f0f325176421c0815
4
- data.tar.gz: 1e20931d726c885a783019e6bd3302633f8871e3
3
+ metadata.gz: df545157946c4036f58eb215212390c7ee6468c2
4
+ data.tar.gz: feef5fae4e44d03d423ab47d79f6b164f90d0a36
5
5
  SHA512:
6
- metadata.gz: 15eff6d3df999006130abf1d31e90723004abbb621bfa1d3c8df011fb21809191b9b68aed94c124255cf9977a5d74fe784e2083b17130fa14f61c10e9bbb28f0
7
- data.tar.gz: 9b59120fcd633bfad9a612f7bb9ad641259348683f0598bfa0a41e73b6d05d65c4363e888debac942141b2eec6ba991a712c7bd23a7e4909c56124c8c0ff7b4d
6
+ metadata.gz: 765b04848c2b7d821bc1dd1c9e23c56e95b1bdad9bdff7a332bcd2d96a3dff1bbe6c0227b751b07a459688107e0511d5ff0394fd09a266297b3c487f2a4b6bf3
7
+ data.tar.gz: c45d06e8676ca91af22d72435e1fdea026dab940fb9e9b6f038b7ed5b0e5c6025e8b29a7aa5bb0cf5251ea1e59c90edcf95a8a5c60c69ac4e836736f6b57b402
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -290,7 +290,7 @@ module Mongo
290
290
  #
291
291
  # @since 2.0.0
292
292
  def h(string)
293
- DIGEST.digest(string)
293
+ digest.digest(string)
294
294
  end
295
295
 
296
296
  # HI algorithm implementation.
@@ -305,7 +305,7 @@ module Mongo
305
305
  data,
306
306
  Base64.strict_decode64(salt),
307
307
  iterations,
308
- DIGEST.size
308
+ digest.size
309
309
  )
310
310
  end
311
311
 
@@ -317,7 +317,7 @@ module Mongo
317
317
  #
318
318
  # @since 2.0.0
319
319
  def hmac(data, key)
320
- OpenSSL::HMAC.digest(DIGEST, data, key)
320
+ OpenSSL::HMAC.digest(digest, data, key)
321
321
  end
322
322
 
323
323
  # Get the iterations from the server response.
@@ -451,6 +451,12 @@ module Mongo
451
451
  raise Unauthorized.new(user) unless reply.documents[0][Operation::Result::OK] == 1
452
452
  @reply = reply
453
453
  end
454
+
455
+ private
456
+
457
+ def digest
458
+ @digest ||= OpenSSL::Digest::SHA1.new.freeze
459
+ end
454
460
  end
455
461
  end
456
462
  end
@@ -99,5 +99,6 @@ require 'mongo/error/socket_error'
99
99
  require 'mongo/error/socket_timeout_error'
100
100
  require 'mongo/error/unchangeable_collection_option'
101
101
  require 'mongo/error/unexpected_chunk_length'
102
+ require 'mongo/error/unexpected_response'
102
103
  require 'mongo/error/missing_file_chunk'
103
104
  require 'mongo/error/unsupported_features'
@@ -0,0 +1,38 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ class Error
17
+
18
+ # Raised if the response read from the socket does not match the latest query.
19
+ #
20
+ # @since 2.2.6
21
+ class UnexpectedResponse < Error
22
+
23
+ # Create the new exception.
24
+ #
25
+ # @example Create the new exception.
26
+ # Mongo::Error::UnexpectedResponse.new(expected_response_to, response_to)
27
+ #
28
+ # @param [ Integer ] expected_response_to The last request id sent.
29
+ # @param [ Integer ] response_to The actual response_to of the reply.
30
+ #
31
+ # @since 2.2.6
32
+ def initialize(expected_response_to, response_to)
33
+ super("Unexpected response. Got response for request ID #{response_to} " +
34
+ "but expected response for request ID #{expected_response_to}")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -82,7 +82,7 @@ module Mongo
82
82
  # @example Get the execution time.
83
83
  # result.time
84
84
  #
85
- # @return [ Integer ] The executiong time in milliseconds.
85
+ # @return [ Integer ] The executing time in milliseconds.
86
86
  #
87
87
  # @since 2.0.0
88
88
  def time
@@ -44,7 +44,7 @@ module Mongo
44
44
  # @since 2.1.0
45
45
  def execute(context)
46
46
  if context.features.users_info_enabled?
47
- UsersInfo.new(spec).execute(context)
47
+ UsersInfo.new(spec).execute(context).validate!
48
48
  else
49
49
  context.with_connection do |connection|
50
50
  Result.new(connection.dispatch([ message(context) ])).validate!
@@ -31,6 +31,12 @@ module Mongo
31
31
  def documents
32
32
  reply.documents.first[USERS]
33
33
  end
34
+
35
+ private
36
+
37
+ def first_document
38
+ @first_document ||= reply.documents[0]
39
+ end
34
40
  end
35
41
  end
36
42
  end
@@ -109,8 +109,8 @@ module Mongo
109
109
  # @param [ IO ] io Stream containing a message
110
110
  #
111
111
  # @return [ Message ] Instance of a Message class
112
- def self.deserialize(io, max_message_size = MAX_MESSAGE_SIZE)
113
- length = deserialize_header(BSON::ByteBuffer.new(io.read(16))).first
112
+ def self.deserialize(io, max_message_size = MAX_MESSAGE_SIZE, expected_response_to = nil)
113
+ length, request_id, response_to, op_code = deserialize_header(BSON::ByteBuffer.new(io.read(16)))
114
114
 
115
115
  # Protection from potential DOS man-in-the-middle attacks. See
116
116
  # DRIVERS-276.
@@ -118,8 +118,15 @@ module Mongo
118
118
  raise Error::MaxMessageSize.new(max_message_size)
119
119
  end
120
120
 
121
+ # Protection against returning the response to a previous request. See
122
+ # RUBY-1117
123
+ if expected_response_to && response_to != expected_response_to
124
+ raise Error::UnexpectedResponse.new(expected_response_to, response_to)
125
+ end
126
+
121
127
  buffer = BSON::ByteBuffer.new(io.read(length - 16))
122
128
  message = allocate
129
+
123
130
  fields.each do |field|
124
131
  if field[:multi]
125
132
  deserialize_array(message, buffer, field)
@@ -228,7 +235,7 @@ module Mongo
228
235
  # @param io [IO] Stream containing the header.
229
236
  # @return [Array<Fixnum>] Deserialized header.
230
237
  def self.deserialize_header(io)
231
- @length, @request_id, @response_to, @op_code = Header.deserialize(io)
238
+ Header.deserialize(io)
232
239
  end
233
240
 
234
241
  # A method for declaring a message field
@@ -67,6 +67,29 @@ module Mongo
67
67
  end
68
68
  end
69
69
 
70
+ # Execute a read operation with a single retry.
71
+ #
72
+ # @api private
73
+ #
74
+ # @example Execute the read.
75
+ # read_with_one_retry do
76
+ # ...
77
+ # end
78
+ #
79
+ # @note This only retries read operations on socket errors.
80
+ #
81
+ # @param [ Proc ] block The block to execute.
82
+ #
83
+ # @return [ Result ] The result of the operation.
84
+ #
85
+ # @since 2.2.6
86
+ def read_with_one_retry(&block)
87
+ block.call
88
+ rescue Error::SocketError,
89
+ Error::SocketTimeoutError
90
+ block.call
91
+ end
92
+
70
93
  # Execute a write operation with a retry.
71
94
  #
72
95
  # @api private
@@ -88,10 +88,11 @@ module Mongo
88
88
  ensure_same_process!
89
89
  connect!
90
90
  begin
91
- yield socket
92
- rescue Exception => e
93
- disconnect!
94
- raise e
91
+ result = yield socket
92
+ success = true
93
+ result
94
+ ensure
95
+ success or disconnect!
95
96
  end
96
97
  end
97
98
 
@@ -102,9 +103,9 @@ module Mongo
102
103
  end
103
104
  end
104
105
 
105
- def read
106
+ def read(request_id = nil)
106
107
  ensure_connected do |socket|
107
- Protocol::Reply.deserialize(socket, max_message_size)
108
+ Protocol::Reply.deserialize(socket, max_message_size, request_id)
108
109
  end
109
110
  end
110
111
  end
@@ -21,6 +21,7 @@ module Mongo
21
21
  class Connection
22
22
  include Connectable
23
23
  include Monitoring::Publishable
24
+ include Retryable
24
25
  extend Forwardable
25
26
 
26
27
  # The ping command.
@@ -62,6 +63,9 @@ module Mongo
62
63
  authenticate!
63
64
  end
64
65
  true
66
+ rescue Mongo::Auth::Unauthorized => e
67
+ disconnect!
68
+ raise e
65
69
  end
66
70
 
67
71
  # Disconnect the connection.
@@ -155,17 +159,29 @@ module Mongo
155
159
 
156
160
  def deliver(messages)
157
161
  write(messages)
158
- messages.last.replyable? ? read : nil
162
+ messages.last.replyable? ? read(messages.last.request_id) : nil
159
163
  end
160
164
 
161
165
  def authenticate!
162
166
  if options[:user]
163
- default_mechanism = @server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr
164
167
  user = Auth::User.new(Options::Redacted.new(:auth_mech => default_mechanism).merge(options))
165
168
  Auth.get(user).login(self)
166
169
  end
167
170
  end
168
171
 
172
+ def default_mechanism
173
+ if socket && socket.connectable?
174
+ socket.write(Monitor::Connection::ISMASTER_BYTES)
175
+ ismaster = Protocol::Reply.deserialize(socket, max_message_size).documents[0]
176
+ min_wire_version = ismaster[Description::MIN_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
177
+ max_wire_version = ismaster[Description::MAX_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
178
+ features = Description::Features.new(min_wire_version..max_wire_version)
179
+ (features.scram_sha_1_enabled? || @server.features.scram_sha_1_enabled?) ? :scram : :mongodb_cr
180
+ else
181
+ @server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr
182
+ end
183
+ end
184
+
169
185
  def write(messages, buffer = BSON::ByteBuffer.new)
170
186
  start_size = 0
171
187
  messages.each do |message|
@@ -103,12 +103,10 @@ module Mongo
103
103
  #
104
104
  # @since 2.0.0
105
105
  def with_connection
106
- begin
107
- connection = checkout
108
- yield(connection)
109
- ensure
110
- checkin(connection) if connection
111
- end
106
+ connection = checkout
107
+ yield(connection)
108
+ ensure
109
+ checkin(connection) if connection
112
110
  end
113
111
 
114
112
  private
@@ -20,6 +20,7 @@ module Mongo
20
20
  #
21
21
  # @since 2.0.0
22
22
  class Connection
23
+ include Retryable
23
24
  include Connectable
24
25
 
25
26
  # The command used for determining server status.
@@ -52,8 +53,10 @@ module Mongo
52
53
  # @since 2.2.0
53
54
  def ismaster
54
55
  ensure_connected do |socket|
55
- socket.write(ISMASTER_BYTES)
56
- Protocol::Reply.deserialize(socket).documents[0]
56
+ read_with_one_retry do
57
+ socket.write(ISMASTER_BYTES)
58
+ Protocol::Reply.deserialize(socket).documents[0]
59
+ end
57
60
  end
58
61
  end
59
62
 
@@ -69,7 +72,7 @@ module Mongo
69
72
  #
70
73
  # @since 2.0.0
71
74
  def connect!
72
- unless socket
75
+ unless socket && socket.connectable?
73
76
  @socket = address.socket(timeout, ssl_options)
74
77
  socket.connect!
75
78
  end
@@ -158,9 +158,9 @@ module Mongo
158
158
  # @since 2.0.0
159
159
  READ_MODE_MAP = {
160
160
  'primary' => :primary,
161
- 'primaryPreferred' => :primary_preferred,
161
+ 'primarypreferred' => :primary_preferred,
162
162
  'secondary' => :secondary,
163
- 'secondaryPreferred' => :secondary_preferred,
163
+ 'secondarypreferred' => :secondary_preferred,
164
164
  'nearest' => :nearest
165
165
  }.freeze
166
166
 
@@ -387,13 +387,12 @@ module Mongo
387
387
  uri_option 'connect', :connect
388
388
 
389
389
  # Auth Options
390
- uri_option 'authsource', :source, :group => :auth, :type => :auth_source
390
+ uri_option 'authsource', :auth_source, :type => :auth_source
391
391
  uri_option 'authmechanism', :auth_mech, :type => :auth_mech
392
- uri_option 'authmechanismproperties', :auth_mech_properties, :group => :auth,
393
- :type => :auth_mech_props
392
+ uri_option 'authmechanismproperties', :auth_mech_properties, :type => :auth_mech_props
394
393
 
395
394
  # Casts option values that do not have a specifically provided
396
- # transofrmation to the appropriate type.
395
+ # transformation to the appropriate type.
397
396
  #
398
397
  # @param value [String] The value to be cast.
399
398
  #
@@ -500,7 +499,7 @@ module Mongo
500
499
  #
501
500
  # @return [Symbol] The transformed authentication mechanism.
502
501
  def auth_mech(value)
503
- AUTH_MECH_MAP[value]
502
+ AUTH_MECH_MAP[value.upcase]
504
503
  end
505
504
 
506
505
  # Read preference mode transformation.
@@ -509,7 +508,7 @@ module Mongo
509
508
  #
510
509
  # @return [Symbol] The read mode symbol.
511
510
  def read_mode(value)
512
- READ_MODE_MAP[value]
511
+ READ_MODE_MAP[value.downcase]
513
512
  end
514
513
 
515
514
  # Read preference tags transformation.
@@ -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.2.5'.freeze
20
+ VERSION = '2.2.6'.freeze
21
21
  end
@@ -31,4 +31,5 @@ Gem::Specification.new do |s|
31
31
  s.bindir = 'bin'
32
32
 
33
33
  s.add_dependency 'bson', '~> 4.0'
34
+ s.add_dependency 'pry'
34
35
  end
@@ -209,11 +209,11 @@ describe Mongo::Address do
209
209
  context 'when providing a DNS entry that resolves to both IPv6 and IPv4' do
210
210
 
211
211
  let(:address) do
212
- described_class.new(DEFAULT_ADDRESS)
212
+ default_address
213
213
  end
214
214
 
215
215
  let(:host) do
216
- DEFAULT_ADDRESS.split(':').first
216
+ address.host
217
217
  end
218
218
 
219
219
  before do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Mongo::Auth::CR do
4
4
 
5
5
  let(:address) do
6
- Mongo::Address.new(DEFAULT_ADDRESS)
6
+ default_address
7
7
  end
8
8
 
9
9
  let(:monitoring) do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Mongo::Auth::LDAP do
4
4
 
5
5
  let(:address) do
6
- Mongo::Address.new(DEFAULT_ADDRESS)
6
+ default_address
7
7
  end
8
8
 
9
9
  let(:monitoring) do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Mongo::Auth::SCRAM do
4
4
 
5
5
  let(:address) do
6
- Mongo::Address.new(DEFAULT_ADDRESS)
6
+ default_address
7
7
  end
8
8
 
9
9
  let(:monitoring) do
@@ -139,5 +139,17 @@ describe Mongo::Auth::User::View do
139
139
  end
140
140
  end
141
141
 
142
+ context 'when a user is not authorized' do
143
+
144
+ let(:view) do
145
+ described_class.new(unauthorized_client.database)
146
+ end
147
+
148
+ it 'raises an OperationFailure' do
149
+ expect{
150
+ view.info('emily')
151
+ }.to raise_exception(Mongo::Error::OperationFailure)
152
+ end
153
+ end
142
154
  end
143
155
  end