mongo 2.11.1 → 2.11.2

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