mongo 2.11.1 → 2.11.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -1
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +24 -0
  5. data/lib/mongo/auth.rb +30 -10
  6. data/lib/mongo/auth/cr.rb +1 -0
  7. data/lib/mongo/auth/cr/conversation.rb +13 -13
  8. data/lib/mongo/auth/ldap.rb +2 -1
  9. data/lib/mongo/auth/ldap/conversation.rb +9 -12
  10. data/lib/mongo/auth/scram.rb +1 -0
  11. data/lib/mongo/auth/scram/conversation.rb +36 -27
  12. data/lib/mongo/auth/x509.rb +2 -1
  13. data/lib/mongo/auth/x509/conversation.rb +9 -9
  14. data/lib/mongo/client.rb +17 -6
  15. data/lib/mongo/cluster.rb +65 -49
  16. data/lib/mongo/cluster/sdam_flow.rb +87 -3
  17. data/lib/mongo/database.rb +1 -1
  18. data/lib/mongo/server.rb +13 -6
  19. data/lib/mongo/server/connection.rb +12 -4
  20. data/lib/mongo/server/connection_base.rb +7 -4
  21. data/lib/mongo/server/description.rb +34 -21
  22. data/lib/mongo/session.rb +10 -10
  23. data/lib/mongo/version.rb +1 -1
  24. data/spec/README.md +13 -0
  25. data/spec/integration/auth_spec.rb +27 -8
  26. data/spec/integration/client_construction_spec.rb +14 -0
  27. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  28. data/spec/mongo/auth/scram/conversation_spec.rb +23 -14
  29. data/spec/mongo/auth/x509/conversation_spec.rb +1 -1
  30. data/spec/mongo/client_construction_spec.rb +1 -21
  31. data/spec/mongo/cluster_spec.rb +38 -0
  32. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  33. data/spec/mongo/server/connection_spec.rb +67 -0
  34. data/spec/runners/sdam/verifier.rb +6 -3
  35. data/spec/spec_tests/data/sdam/rs/primary_address_change.yml +29 -0
  36. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +27 -23
  37. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +56 -79
  38. data/spec/spec_tests/data/sdam/sharded/primary_address_change.yml +21 -0
  39. data/spec/spec_tests/data/sdam/sharded/primary_mismatched_me.yml +22 -0
  40. data/spec/spec_tests/data/sdam/single/primary_address_change.yml +24 -0
  41. data/spec/spec_tests/data/sdam/single/primary_mismatched_me.yml +25 -0
  42. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +159 -0
  43. data/spec/spec_tests/data/sdam_monitoring/{replica_set_other_seed.yml → replica_set_with_primary_change.yml} +97 -101
  44. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary_removal.yml +22 -18
  45. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +90 -0
  46. data/spec/support/cluster_config.rb +36 -0
  47. data/spec/support/constraints.rb +18 -18
  48. data/spec/support/server_discovery_and_monitoring.rb +2 -0
  49. metadata +18 -4
  50. 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: d52eaac2e54a90b6aad27aa1ef965c67cb36d4ea61907092e867b40eac1f9539
4
+ data.tar.gz: c118c191fac15467052a316eadd13035ed43968af1e46bd50ea45e6ad4884900
5
5
  SHA512:
6
- metadata.gz: 415ae16f9f26a04e7ed9d9d29f264f2632a8cfca230849a11152f8bc62a722aa4a7e600ca5db217e26e695ca2b270a54973bb9c339af67f1f3880c67ed917cc4
7
- data.tar.gz: '058ec4695e331e142f99feee1a59082494ed91e80d55490985e8eee650f1d94ba767d581ea08180ccff18c93300642ea59ca91dc1fe44da6771fd4ca2a94d27f'
6
+ metadata.gz: f652bbcf6b01f172199d467953d8f0ab3b9bc83b2e790d87a2c2f3b3251e5c0139234cbe1c2a1fbb6791ab5bf18be001b9be271ef0a0a9bfa75f9d20be9441f5
7
+ data.tar.gz: 82a0273865415d0ce911b96a0f80fa59610539d23e8182acd706cade06226df0d9a5ca931a3ae1b05a6191e4d4e934cb713c232a67c4dcedb48d03cf696ed79b
@@ -1 +1,2 @@
1
- �̚�r��Ij���6A(���?��eD�=�^����xA[u�g�+�4o(����s�(���!͠/f4T)Qa&'��󍃴a� 7�Fe��zP���m�@��v?���j89ד=�o#V?|�K�3aR۩#���z6^Y�-Gm���OcOs�(��u"����
1
+ Vn��i�'P��zQ��il|�� )8i�=���:\ʜ���ڭ)��V)�8���uH2�����q8D�����k���u�s@��`:&V�2'0$ť'�aޒ@{j����gI�.}�Ո�Y~�y8~�>��QX-��)2N��r
2
+ ����E�Czꅚ �n�,��y�ڋ�X��Ϊr4���]V�
data.tar.gz.sig CHANGED
Binary file
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')
@@ -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
@@ -20,6 +20,7 @@ module Mongo
20
20
  # Defines behavior for X.509 authentication.
21
21
  #
22
22
  # @since 2.0.0
23
+ # @api private
23
24
  class X509
24
25
 
25
26
  # The authentication mechinism string.
@@ -67,7 +68,7 @@ module Mongo
67
68
  conversation = Conversation.new(user)
68
69
  reply = connection.dispatch([ conversation.start(connection) ])
69
70
  connection.update_cluster_time(Operation::Result.new(reply))
70
- conversation.finalize(reply)
71
+ conversation.finalize(reply, connection)
71
72
  end
72
73
  end
73
74
  end
@@ -42,26 +42,26 @@ module Mongo
42
42
  #
43
43
  # @param [ Protocol::Message ] reply The reply of the previous
44
44
  # message.
45
+ # @param [ Server::Connection ] connection The connection being
46
+ # authenticated.
45
47
  #
46
48
  # @return [ Protocol::Query ] The next message to send.
47
49
  #
48
50
  # @since 2.0.0
49
- def finalize(reply)
50
- validate!(reply)
51
+ def finalize(reply, connection)
52
+ validate!(reply, connection.server)
51
53
  end
52
54
 
53
55
  # Start the X.509 conversation. This returns the first message that
54
56
  # needs to be sent to the server.
55
57
  #
56
- # @example Start the conversation.
57
- # conversation.start
58
- #
59
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
58
+ # @param [ Server::Connection ] connection The connection being
59
+ # authenticated.
60
60
  #
61
61
  # @return [ Protocol::Query ] The first X.509 conversation message.
62
62
  #
63
63
  # @since 2.0.0
64
- def start(connection = nil)
64
+ def start(connection)
65
65
  login = LOGIN.merge(mechanism: X509::MECHANISM)
66
66
  login[:user] = user.name if user.name
67
67
  if connection && connection.features.op_msg_enabled?
@@ -103,9 +103,9 @@ module Mongo
103
103
 
104
104
  private
105
105
 
106
- def validate!(reply)
106
+ def validate!(reply, server)
107
107
  if reply.documents[0][Operation::Result::OK] != 1
108
- raise Unauthorized.new(user, used_mechanism: MECHANISM)
108
+ raise Unauthorized.new(user, used_mechanism: MECHANISM, server: server)
109
109
  end
110
110
  @reply = reply
111
111
  end