mongo 2.2.5 → 2.2.6

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