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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33db1e0c2cfe7d7de3da0fbc3b24deac1ab01c0ad3cfc07239616bda20bd3e21
4
- data.tar.gz: 6923e1d8fff7313a7f1a5a73e5a23fcebc6fa9b2b3d083e7f7468928ff2d76f1
3
+ metadata.gz: a2f8d74abb00ab2e9753a3918841a7242a83e956c91f88fe5182febd2fba7897
4
+ data.tar.gz: 6de9cab3c254bec7adba15f964806188d8eb664100dd0aa1fe0d6608841ae2de
5
5
  SHA512:
6
- metadata.gz: 415ae16f9f26a04e7ed9d9d29f264f2632a8cfca230849a11152f8bc62a722aa4a7e600ca5db217e26e695ca2b270a54973bb9c339af67f1f3880c67ed917cc4
7
- data.tar.gz: '058ec4695e331e142f99feee1a59082494ed91e80d55490985e8eee650f1d94ba767d581ea08180ccff18c93300642ea59ca91dc1fe44da6771fd4ca2a94d27f'
6
+ metadata.gz: 7c1e95f38f68232e57fc3f39195053dfaa7ac1aa1e0b2f80bea847bbaf0db108c671a4cca5ccdb92951822203de784bd4d8152d3721edf7424da43ed20586800
7
+ data.tar.gz: 1c7ce8ac18fb2a899166fca64831c9cc18fee6b217f60f0dbfb1006f6b7ef831b34a5d6e7e1a39414b3e1109cb6fc76e0eff90c2a359166b6cfb074bfceedd5a
@@ -1 +1,2 @@
1
- �̚�r��Ij���6A(���?��e�D�=�^����xA[u�g�+�4o(����s�(���!͠/f4T)Qa&'��󍃴a� 7�Fe��zP���m�@��v?���j89ד=�o#V?|�K�3aR۩#���z6^Y�-Gm���OcO�s�(��u"����
1
+ '?��iḫ����K�]��f�,VI8>H�썟��*��4 �&�O�K>4u `+/� Ql
2
+ m�2�z��xR��BY��*E�Y8�^���g��J�� �"l�b4&�S�ZE\9�g�>?&/��$Uc �O��$=`�dB\.����~�ѵ�盀Z�Ug��88Q�<O �"`����0B����콰�-�mؑ���c�����~�6!(�w@�l-!�����~�Cڹa��mZ�f�2���1�'S�
data.tar.gz.sig CHANGED
@@ -1 +1,2 @@
1
- ���@�}�!UH����p�5��@�k��~|�rB�K��;8���&?�s���|Gb��"U1R�Ο����32�Qh�� A48}+���a�)�������])Y�"�`g��ML��x֓�xb����w���3\�9=����ɇ02���"w����� �����*e � ����};FŌ,� B;'&[M��%���Kp�'
1
+ m���7����5��g����`Z��8�JS�9ˉWi֝Im!{6\�ߘF�A����
2
+ (�]>/o�V���lF�(�@
data/Rakefile CHANGED
@@ -34,6 +34,30 @@ namespace :spec do
34
34
  SpecSetup.new.run
35
35
  end
36
36
 
37
+ desc 'Waits for sessions to be available in the deployment'
38
+ task :wait_for_sessions do
39
+ $: << File.join(File.dirname(__FILE__), 'spec')
40
+
41
+ require 'support/utils'
42
+ require 'support/spec_config'
43
+ require 'support/client_registry'
44
+
45
+ client = ClientRegistry.instance.global_client('authorized')
46
+ client.database.command(ping: 1)
47
+ deadline = Time.now + 300
48
+ while Time.now < deadline
49
+ if client.cluster.send(:sessions_supported?)
50
+ break
51
+ end
52
+ sleep 1
53
+ client.close
54
+ client.reconnect
55
+ end
56
+ unless client.cluster.send(:sessions_supported?)
57
+ raise "Sessions did not become supported in the allowed time"
58
+ end
59
+ end
60
+
37
61
  desc 'Prints configuration used by the test suite'
38
62
  task :config do
39
63
  $: << File.join(File.dirname(__FILE__), 'spec')
@@ -66,6 +66,8 @@ module Mongo
66
66
  # @param [ String ] seed The provided address.
67
67
  # @param [ Hash ] options The address options.
68
68
  #
69
+ # @option options [ Float ] :connect_timeout Connect timeout.
70
+ #
69
71
  # @since 2.0.0
70
72
  def initialize(seed, options = {})
71
73
  if seed.nil?
@@ -85,6 +87,9 @@ module Mongo
85
87
  # @return [ Integer ] port The port.
86
88
  attr_reader :port
87
89
 
90
+ # @api private
91
+ attr_reader :options
92
+
88
93
  # Check equality of the address to another.
89
94
  #
90
95
  # @example Check address equality.
@@ -138,13 +143,30 @@ module Mongo
138
143
  "#<Mongo::Address:0x#{object_id} address=#{to_s}>"
139
144
  end
140
145
 
141
- # Get a socket for the provided address, given the options.
146
+ # Get a socket for the address stored in this object, given the options.
147
+ #
148
+ # If the address stored in this object looks like a Unix path, this method
149
+ # returns a Unix domain socket for this path.
142
150
  #
143
- # The address the socket connects to is determined by the algorithm described in the
144
- # #intialize_resolver! documentation. Each time this method is called, #initialize_resolver!
145
- # will be called, meaning that a new hostname lookup will occur. This is done so that any
146
- # changes to which addresses the hostname resolves to will be picked up even if a socket has
147
- # been connected to it before.
151
+ # Otherwise, this method attempts to resolve the address stored in
152
+ # this object to IPv4 and IPv6 addresses using +Socket#getaddrinfo+, then
153
+ # connects to the resulting addresses and returns the socket of the first
154
+ # successful connection. The order in which address families (IPv4/IPV6)
155
+ # are tried is the same order in which the addresses are returned by
156
+ # +getaddrinfo+, and is determined by the host system.
157
+ #
158
+ # Name resolution is performed on each +socket+ call. This is done so that
159
+ # any changes to which addresses the host names used as seeds or in
160
+ # server configuration resolve to are immediately noticed by the driver,
161
+ # even if a socket has been connected to the affected host name/address
162
+ # before. However, note that DNS TTL values may still affect when a change
163
+ # to a host address is noticed by the driver.
164
+ #
165
+ # This method propagates any exceptions raised during DNS resolution and
166
+ # subsequent connection attempts. In case of a host name resolving to
167
+ # multiple IP addresses, the error raised by the last attempt is propagated
168
+ # to the caller. This method does not map exceptions to Mongo::Error
169
+ # subclasses, and may raise any subclass of Exception.
148
170
  #
149
171
  # @example Get a socket.
150
172
  # address.socket(5, :ssl => true)
@@ -155,11 +177,34 @@ module Mongo
155
177
  #
156
178
  # @option options [ Float ] :connect_timeout Connect timeout.
157
179
  #
158
- # @return [ Mongo::Socket::SSL, Mongo::Socket::TCP, Mongo::Socket::Unix ] The socket.
180
+ # @return [ Mongo::Socket::SSL | Mongo::Socket::TCP | Mongo::Socket::Unix ]
181
+ # The socket.
182
+ #
183
+ # @raise [ Exception ] If network connection failed.
159
184
  #
160
185
  # @since 2.0.0
161
186
  def socket(socket_timeout, ssl_options = {}, options = {})
162
- create_resolver(ssl_options).socket(socket_timeout, ssl_options, options)
187
+ if seed.downcase =~ Unix::MATCH
188
+ specific_address = Unix.new(seed.downcase)
189
+ return specific_address.socket(socket_timeout, ssl_options, options)
190
+ end
191
+
192
+ options = {
193
+ connect_timeout: Server::CONNECT_TIMEOUT,
194
+ }.update(options)
195
+
196
+ family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
197
+ error = nil
198
+ ::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM).each do |info|
199
+ begin
200
+ specific_address = FAMILY_MAP[info[4]].new(info[3], port, host)
201
+ socket = specific_address.socket(socket_timeout, ssl_options, options)
202
+ return socket
203
+ rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
204
+ error = e
205
+ end
206
+ end
207
+ raise error
163
208
  end
164
209
 
165
210
  # Get the address as a string.
@@ -182,37 +227,8 @@ module Mongo
182
227
  end
183
228
  end
184
229
 
185
- # @api private
186
- def connect_timeout
187
- @connect_timeout ||= @options[:connect_timeout] || Server::CONNECT_TIMEOUT
188
- end
189
-
190
230
  private
191
231
 
192
- # To determine which address the socket will connect to, the driver will
193
- # attempt to connect to each IP address returned by Socket::getaddrinfo in
194
- # sequence. Once a successful connection is made, a resolver with that
195
- # IP address specified is returned. If no successful connection is
196
- # made, the error made by the last connection attempt is raised.
197
- def create_resolver(ssl_options)
198
- return Unix.new(seed.downcase) if seed.downcase =~ Unix::MATCH
199
-
200
- family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
201
- error = nil
202
- ::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM).each do |info|
203
- begin
204
- specific_address = FAMILY_MAP[info[4]].new(info[3], port, host)
205
- socket = specific_address.socket(
206
- connect_timeout, ssl_options, connect_timeout: connect_timeout)
207
- socket.close
208
- return specific_address
209
- rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
210
- error = e
211
- end
212
- end
213
- raise error
214
- end
215
-
216
232
  def parse_host_port
217
233
  address = seed.downcase
218
234
  case address
@@ -110,25 +110,45 @@ module Mongo
110
110
  # @param [ String ] used_mechanism Auth mechanism actually used for
111
111
  # authentication. This is a full string like SCRAM-SHA-256.
112
112
  # @param [ String ] message The error message returned by the server.
113
+ # @param [ Server ] server The server instance that authentication
114
+ # was attempted against.
113
115
  #
114
116
  # @since 2.0.0
115
- def initialize(user, used_mechanism: nil, message: nil)
116
- specified_mechanism = if user.mechanism
117
- " (mechanism: #{user.mechanism})"
118
- else
119
- ''
117
+ def initialize(user, used_mechanism: nil, message: nil,
118
+ server: nil
119
+ )
120
+ configured_bits = []
121
+ used_bits = [
122
+ "auth source: #{user.auth_source}",
123
+ ]
124
+
125
+ if user.mechanism
126
+ configured_bits << "mechanism: #{user.mechanism}"
120
127
  end
121
- used_mechanism = if used_mechanism
122
- " (used mechanism: #{used_mechanism})"
123
- else
124
- ''
128
+
129
+ if used_mechanism
130
+ used_bits << "used mechanism: #{used_mechanism}"
131
+ end
132
+
133
+ if server
134
+ used_bits << "used server: #{server.address} (#{server.status})"
125
135
  end
136
+
126
137
  used_user = if user.mechanism == :mongodb_x509
127
138
  'Client certificate'
128
139
  else
129
140
  "User #{user.name}"
130
141
  end
131
- msg = "#{used_user}#{specified_mechanism} is not authorized to access #{user.database} (auth source: #{user.auth_source})#{used_mechanism}"
142
+
143
+ if configured_bits.empty?
144
+ configured_bits = ''
145
+ else
146
+ configured_bits = " (#{configured_bits.join(', ')})"
147
+ end
148
+
149
+ used_bits = " (#{used_bits.join(', ')})"
150
+
151
+ msg = "#{used_user}#{configured_bits} is not authorized to access #{user.database}#{used_bits}"
132
152
  if message
133
153
  msg += ': ' + message
134
154
  end
@@ -23,6 +23,7 @@ module Mongo
23
23
  # @deprecated MONGODB-CR authentication mechanism is deprecated
24
24
  # as of MongoDB 3.6. Support for it in the Ruby driver will be
25
25
  # removed in driver version 3.0. Please use SCRAM instead.
26
+ # @api private
26
27
  class CR
27
28
 
28
29
  # The authentication mechinism string.
@@ -52,13 +52,14 @@ module Mongo
52
52
  #
53
53
  # @param [ Protocol::Message ] reply The reply of the previous
54
54
  # message.
55
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
55
+ # @param [ Mongo::Server::Connection ] connection The connection being
56
+ # authenticated.
56
57
  #
57
58
  # @return [ Protocol::Query ] The next message to send.
58
59
  #
59
60
  # @since 2.0.0
60
- def continue(reply, connection = nil)
61
- validate!(reply)
61
+ def continue(reply, connection)
62
+ validate!(reply, connection.server)
62
63
  if connection && connection.features.op_msg_enabled?
63
64
  selector = LOGIN.merge(user: user.name, nonce: nonce, key: user.auth_key(nonce))
64
65
  selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
@@ -78,29 +79,28 @@ module Mongo
78
79
  # Finalize the CR conversation. This is meant to be iterated until
79
80
  # the provided reply indicates the conversation is finished.
80
81
  #
81
- # @example Finalize the conversation.
82
- # conversation.finalize(reply)
83
- #
84
82
  # @param [ Protocol::Message ] reply The reply of the previous
85
83
  # message.
84
+ # @param [ Server::Connection ] connection The connection being
85
+ # authenticated.
86
86
  #
87
87
  # @return [ Protocol::Query ] The next message to send.
88
88
  #
89
89
  # @since 2.0.0
90
- def finalize(reply, connection = nil)
91
- validate!(reply)
90
+ def finalize(reply, connection)
91
+ validate!(reply, connection.server)
92
92
  end
93
93
 
94
94
  # Start the CR conversation. This returns the first message that
95
95
  # needs to be sent to the server.
96
96
  #
97
- # @example Start the conversation.
98
- # conversation.start
97
+ # @param [ Server::Connection ] connection The connection being
98
+ # authenticated.
99
99
  #
100
100
  # @return [ Protocol::Query ] The first CR conversation message.
101
101
  #
102
102
  # @since 2.0.0
103
- def start(connection = nil)
103
+ def start(connection)
104
104
  if connection && connection.features.op_msg_enabled?
105
105
  selector = Auth::GET_NONCE.merge(Protocol::Msg::DATABASE_IDENTIFIER => user.auth_source)
106
106
  cluster_time = connection.mongos? && connection.cluster_time
@@ -129,9 +129,9 @@ module Mongo
129
129
 
130
130
  private
131
131
 
132
- def validate!(reply)
132
+ def validate!(reply, server)
133
133
  if reply.documents[0][Operation::Result::OK] != 1
134
- raise Unauthorized.new(user, used_mechanism: MECHANISM)
134
+ raise Unauthorized.new(user, used_mechanism: MECHANISM, server: server)
135
135
  end
136
136
  @nonce = reply.documents[0][Auth::NONCE]
137
137
  @reply = reply
@@ -20,6 +20,7 @@ module Mongo
20
20
  # Defines behavior for LDAP Proxy authentication.
21
21
  #
22
22
  # @since 2.0.0
23
+ # @api private
23
24
  class LDAP
24
25
 
25
26
  # The authentication mechinism string.
@@ -56,7 +57,7 @@ module Mongo
56
57
  conversation = Conversation.new(user)
57
58
  reply = connection.dispatch([ conversation.start(connection) ])
58
59
  connection.update_cluster_time(Operation::Result.new(reply))
59
- conversation.finalize(reply)
60
+ conversation.finalize(reply, connection)
60
61
  end
61
62
  end
62
63
  end
@@ -37,31 +37,28 @@ module Mongo
37
37
  # Finalize the PLAIN conversation. This is meant to be iterated until
38
38
  # the provided reply indicates the conversation is finished.
39
39
  #
40
- # @example Finalize the conversation.
41
- # conversation.finalize(reply)
42
- #
43
40
  # @param [ Protocol::Message ] reply The reply of the previous
44
41
  # message.
42
+ # @param [ Server::Connection ] connection The connection being
43
+ # authenticated.
45
44
  #
46
45
  # @return [ Protocol::Query ] The next message to send.
47
46
  #
48
47
  # @since 2.0.0
49
- def finalize(reply)
50
- validate!(reply)
48
+ def finalize(reply, connection)
49
+ validate!(reply, connection.server)
51
50
  end
52
51
 
53
52
  # Start the PLAIN conversation. This returns the first message that
54
53
  # needs to be sent to the server.
55
54
  #
56
- # @example Start the conversation.
57
- # conversation.start
58
- #
59
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
55
+ # @param [ Server::Connection ] connection The connection being
56
+ # authenticated.
60
57
  #
61
58
  # @return [ Protocol::Query ] The first PLAIN conversation message.
62
59
  #
63
60
  # @since 2.0.0
64
- def start(connection = nil)
61
+ def start(connection)
65
62
  if connection && connection.features.op_msg_enabled?
66
63
  selector = LOGIN.merge(payload: payload, mechanism: LDAP::MECHANISM)
67
64
  selector[Protocol::Msg::DATABASE_IDENTIFIER] = Auth::EXTERNAL
@@ -96,9 +93,9 @@ module Mongo
96
93
  BSON::Binary.new("\x00#{user.name}\x00#{user.password}")
97
94
  end
98
95
 
99
- def validate!(reply)
96
+ def validate!(reply, server)
100
97
  if reply.documents[0][Operation::Result::OK] != 1
101
- raise Unauthorized.new(user, used_mechanism: MECHANISM)
98
+ raise Unauthorized.new(user, used_mechanism: MECHANISM, server: server)
102
99
  end
103
100
  @reply = reply
104
101
  end
@@ -20,6 +20,7 @@ module Mongo
20
20
  # Defines behavior for SCRAM authentication.
21
21
  #
22
22
  # @since 2.0.0
23
+ # @api private
23
24
  class SCRAM
24
25
 
25
26
  # The authentication mechanism string for SCRAM-SHA-1.
@@ -20,6 +20,7 @@ module Mongo
20
20
  # the client and server.
21
21
  #
22
22
  # @since 2.0.0
23
+ # @api private
23
24
  class Conversation
24
25
 
25
26
  # The base client continue message.
@@ -103,13 +104,14 @@ module Mongo
103
104
  #
104
105
  # @param [ Protocol::Message ] reply The reply of the previous
105
106
  # message.
106
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
107
+ # @param [ Server::Connection ] connection The connection being
108
+ # authenticated.
107
109
  #
108
- # @return [ Protocol::Query ] The next message to send.
110
+ # @return [ Protocol::Message ] The next message to send.
109
111
  #
110
112
  # @since 2.0.0
111
- def continue(reply, connection = nil)
112
- validate_first_message!(reply)
113
+ def continue(reply, connection)
114
+ validate_first_message!(reply, connection.server)
113
115
 
114
116
  # The salted password needs to be calculated now; otherwise, if the
115
117
  # client key is cached from a previous authentication, the salt in the
@@ -118,7 +120,10 @@ module Mongo
118
120
  salted_password
119
121
 
120
122
  if connection && connection.features.op_msg_enabled?
121
- selector = CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id)
123
+ selector = CLIENT_CONTINUE_MESSAGE.merge(
124
+ payload: client_final_message,
125
+ conversationId: id,
126
+ )
122
127
  selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
123
128
  cluster_time = connection.mongos? && connection.cluster_time
124
129
  selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
@@ -127,8 +132,11 @@ module Mongo
127
132
  Protocol::Query.new(
128
133
  user.auth_source,
129
134
  Database::COMMAND,
130
- CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id),
131
- limit: -1
135
+ CLIENT_CONTINUE_MESSAGE.merge(
136
+ payload: client_final_message,
137
+ conversationId: id,
138
+ ),
139
+ limit: -1,
132
140
  )
133
141
  end
134
142
  end
@@ -136,20 +144,20 @@ module Mongo
136
144
  # Finalize the SCRAM conversation. This is meant to be iterated until
137
145
  # the provided reply indicates the conversation is finished.
138
146
  #
139
- # @example Finalize the conversation.
140
- # conversation.finalize(reply)
141
- #
142
147
  # @param [ Protocol::Message ] reply The reply of the previous
143
148
  # message.
144
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
149
+ # @param [ Server::Connection ] connection The connection being authenticated.
145
150
  #
146
151
  # @return [ Protocol::Query ] The next message to send.
147
152
  #
148
153
  # @since 2.0.0
149
- def finalize(reply, connection = nil)
150
- validate_final_message!(reply)
154
+ def finalize(reply, connection)
155
+ validate_final_message!(reply, connection.server)
151
156
  if connection && connection.features.op_msg_enabled?
152
- selector = CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id)
157
+ selector = CLIENT_CONTINUE_MESSAGE.merge(
158
+ payload: client_empty_message,
159
+ conversationId: id,
160
+ )
153
161
  selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
154
162
  cluster_time = connection.mongos? && connection.cluster_time
155
163
  selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
@@ -158,8 +166,11 @@ module Mongo
158
166
  Protocol::Query.new(
159
167
  user.auth_source,
160
168
  Database::COMMAND,
161
- CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id),
162
- limit: -1
169
+ CLIENT_CONTINUE_MESSAGE.merge(
170
+ payload: client_empty_message,
171
+ conversationId: id,
172
+ ),
173
+ limit: -1,
163
174
  )
164
175
  end
165
176
  end
@@ -167,15 +178,12 @@ module Mongo
167
178
  # Start the SCRAM conversation. This returns the first message that
168
179
  # needs to be sent to the server.
169
180
  #
170
- # @example Start the conversation.
171
- # conversation.start
172
- #
173
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
181
+ # @param [ Server::Connection ] connection The connection being authenticated.
174
182
  #
175
183
  # @return [ Protocol::Query ] The first SCRAM conversation message.
176
184
  #
177
185
  # @since 2.0.0
178
- def start(connection = nil)
186
+ def start(connection)
179
187
  if connection && connection.features.op_msg_enabled?
180
188
  selector = CLIENT_FIRST_MESSAGE.merge(
181
189
  payload: client_first_message, mechanism: full_mechanism)
@@ -189,7 +197,7 @@ module Mongo
189
197
  Database::COMMAND,
190
198
  CLIENT_FIRST_MESSAGE.merge(
191
199
  payload: client_first_message, mechanism: full_mechanism),
192
- limit: -1
200
+ limit: -1,
193
201
  )
194
202
  end
195
203
  end
@@ -505,23 +513,24 @@ module Mongo
505
513
  check == 0
506
514
  end
507
515
 
508
- def validate_final_message!(reply)
509
- validate!(reply)
516
+ def validate_final_message!(reply, server)
517
+ validate!(reply, server)
510
518
  unless compare_digest(verifier, server_signature)
511
519
  raise Error::InvalidSignature.new(verifier, server_signature)
512
520
  end
513
521
  end
514
522
 
515
- def validate_first_message!(reply)
516
- validate!(reply)
523
+ def validate_first_message!(reply, server)
524
+ validate!(reply, server)
517
525
  raise Error::InvalidNonce.new(nonce, rnonce) unless rnonce.start_with?(nonce)
518
526
  end
519
527
 
520
- def validate!(reply)
528
+ def validate!(reply, server)
521
529
  if reply.documents[0][Operation::Result::OK] != 1
522
530
  raise Unauthorized.new(user,
523
531
  used_mechanism: full_mechanism,
524
532
  message: reply.documents[0]['errmsg'],
533
+ server: server,
525
534
  )
526
535
  end
527
536
  @reply = reply