mongo 2.4.3 → 2.5.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -2
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +3 -2
  5. data/lib/mongo/auth/cr.rb +6 -4
  6. data/lib/mongo/auth/cr/conversation.rb +33 -17
  7. data/lib/mongo/auth/ldap.rb +4 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +19 -9
  9. data/lib/mongo/auth/scram.rb +7 -4
  10. data/lib/mongo/auth/scram/conversation.rb +62 -24
  11. data/lib/mongo/auth/user.rb +10 -0
  12. data/lib/mongo/auth/user/view.rb +44 -22
  13. data/lib/mongo/auth/x509.rb +4 -2
  14. data/lib/mongo/auth/x509/conversation.rb +19 -9
  15. data/lib/mongo/bulk_write.rb +33 -27
  16. data/lib/mongo/bulk_write/combineable.rb +5 -0
  17. data/lib/mongo/bulk_write/transformable.rb +2 -0
  18. data/lib/mongo/bulk_write/validatable.rb +4 -0
  19. data/lib/mongo/client.rb +123 -12
  20. data/lib/mongo/cluster.rb +52 -11
  21. data/lib/mongo/cluster/app_metadata.rb +8 -2
  22. data/lib/mongo/cluster/cursor_reaper.rb +0 -1
  23. data/lib/mongo/cluster/topology.rb +1 -1
  24. data/lib/mongo/collection.rb +114 -27
  25. data/lib/mongo/collection/view.rb +8 -2
  26. data/lib/mongo/collection/view/aggregation.rb +11 -7
  27. data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
  28. data/lib/mongo/collection/view/builder/find_command.rb +5 -3
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
  30. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  31. data/lib/mongo/collection/view/change_stream.rb +160 -0
  32. data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
  33. data/lib/mongo/collection/view/iterable.rb +11 -10
  34. data/lib/mongo/collection/view/map_reduce.rb +22 -18
  35. data/lib/mongo/collection/view/readable.rb +51 -37
  36. data/lib/mongo/collection/view/writable.rb +72 -40
  37. data/lib/mongo/cursor.rb +25 -4
  38. data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
  39. data/lib/mongo/database.rb +22 -11
  40. data/lib/mongo/database/view.rb +16 -12
  41. data/lib/mongo/error.rb +5 -0
  42. data/lib/mongo/error/invalid_session.rb +36 -0
  43. data/lib/mongo/error/missing_resume_token.rb +39 -0
  44. data/lib/mongo/error/operation_failure.rb +17 -0
  45. data/lib/mongo/error/parser.rb +3 -2
  46. data/lib/mongo/error/unknown_payload_type.rb +41 -0
  47. data/lib/mongo/error/unsupported_array_filters.rb +51 -0
  48. data/lib/mongo/error/unsupported_message_type.rb +23 -0
  49. data/lib/mongo/grid/fs_bucket.rb +5 -4
  50. data/lib/mongo/grid/stream/read.rb +3 -2
  51. data/lib/mongo/grid/stream/write.rb +2 -2
  52. data/lib/mongo/index/view.rb +35 -25
  53. data/lib/mongo/monitoring/event/secure.rb +14 -0
  54. data/lib/mongo/operation.rb +16 -0
  55. data/lib/mongo/operation/commands.rb +1 -0
  56. data/lib/mongo/operation/commands/aggregate.rb +9 -5
  57. data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
  58. data/lib/mongo/operation/commands/collections_info.rb +6 -6
  59. data/lib/mongo/operation/commands/command.rb +2 -1
  60. data/lib/mongo/operation/commands/create.rb +6 -2
  61. data/lib/mongo/operation/commands/drop.rb +6 -2
  62. data/lib/mongo/operation/commands/drop_database.rb +6 -2
  63. data/lib/mongo/operation/commands/explain.rb +27 -0
  64. data/lib/mongo/operation/commands/explain/result.rb +52 -0
  65. data/lib/mongo/operation/commands/indexes.rb +1 -1
  66. data/lib/mongo/operation/commands/list_collections.rb +1 -1
  67. data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
  68. data/lib/mongo/operation/commands/list_indexes.rb +1 -1
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
  70. data/lib/mongo/operation/commands/map_reduce.rb +8 -4
  71. data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
  72. data/lib/mongo/operation/commands/user_query.rb +1 -1
  73. data/lib/mongo/operation/commands/users_info.rb +6 -2
  74. data/lib/mongo/operation/executable.rb +4 -1
  75. data/lib/mongo/operation/read_preference.rb +10 -5
  76. data/lib/mongo/operation/result.rb +26 -2
  77. data/lib/mongo/operation/specifiable.rb +13 -1
  78. data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
  79. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
  80. data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
  81. data/lib/mongo/operation/write/command/create_index.rb +6 -1
  82. data/lib/mongo/operation/write/command/delete.rb +28 -4
  83. data/lib/mongo/operation/write/command/drop_index.rb +6 -1
  84. data/lib/mongo/operation/write/command/insert.rb +22 -18
  85. data/lib/mongo/operation/write/command/update.rb +24 -9
  86. data/lib/mongo/operation/write/command/writable.rb +14 -1
  87. data/lib/mongo/operation/write/insert.rb +4 -1
  88. data/lib/mongo/operation/write/insert/result.rb +2 -2
  89. data/lib/mongo/operation/write/update.rb +7 -1
  90. data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
  91. data/lib/mongo/protocol.rb +3 -0
  92. data/lib/mongo/protocol/bit_vector.rb +2 -2
  93. data/lib/mongo/protocol/compressed.rb +135 -0
  94. data/lib/mongo/protocol/delete.rb +8 -6
  95. data/lib/mongo/protocol/get_more.rb +8 -6
  96. data/lib/mongo/protocol/insert.rb +8 -6
  97. data/lib/mongo/protocol/kill_cursors.rb +8 -6
  98. data/lib/mongo/protocol/message.rb +31 -3
  99. data/lib/mongo/protocol/msg.rb +172 -0
  100. data/lib/mongo/protocol/query.rb +26 -6
  101. data/lib/mongo/protocol/registry.rb +76 -0
  102. data/lib/mongo/protocol/reply.rb +10 -5
  103. data/lib/mongo/protocol/serializers.rb +224 -0
  104. data/lib/mongo/protocol/update.rb +8 -6
  105. data/lib/mongo/retryable.rb +4 -2
  106. data/lib/mongo/server.rb +6 -3
  107. data/lib/mongo/server/connectable.rb +1 -1
  108. data/lib/mongo/server/connection.rb +30 -8
  109. data/lib/mongo/server/description.rb +25 -1
  110. data/lib/mongo/server/description/features.rb +4 -1
  111. data/lib/mongo/server/monitor.rb +5 -0
  112. data/lib/mongo/server/monitor/connection.rb +50 -2
  113. data/lib/mongo/server_selector/nearest.rb +10 -4
  114. data/lib/mongo/server_selector/primary.rb +20 -0
  115. data/lib/mongo/server_selector/primary_preferred.rb +10 -4
  116. data/lib/mongo/server_selector/secondary.rb +10 -4
  117. data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
  118. data/lib/mongo/session.rb +180 -0
  119. data/lib/mongo/session/server_session.rb +73 -0
  120. data/lib/mongo/session/session_pool.rb +161 -0
  121. data/lib/mongo/uri.rb +11 -0
  122. data/lib/mongo/version.rb +1 -1
  123. data/mongo.gemspec +2 -1
  124. data/spec/mongo/auth/cr_spec.rb +12 -0
  125. data/spec/mongo/auth/ldap_spec.rb +2 -0
  126. data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
  127. data/spec/mongo/auth/scram_spec.rb +25 -1
  128. data/spec/mongo/auth/user/view_spec.rb +268 -76
  129. data/spec/mongo/auth/x509_spec.rb +2 -0
  130. data/spec/mongo/bulk_write_spec.rb +435 -5
  131. data/spec/mongo/client_spec.rb +356 -39
  132. data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
  133. data/spec/mongo/cluster_spec.rb +176 -0
  134. data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
  135. data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
  136. data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
  137. data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
  138. data/spec/mongo/collection/view/readable_spec.rb +3 -12
  139. data/spec/mongo/collection_spec.rb +1048 -42
  140. data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
  141. data/spec/mongo/cursor_spec.rb +2 -2
  142. data/spec/mongo/database_spec.rb +50 -1
  143. data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
  144. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  145. data/spec/mongo/index/view_spec.rb +146 -8
  146. data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
  147. data/spec/mongo/operation/read/query_spec.rb +2 -1
  148. data/spec/mongo/operation/specifiable_spec.rb +2 -2
  149. data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
  150. data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
  151. data/spec/mongo/operation/write/command/update_spec.rb +93 -10
  152. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  153. data/spec/mongo/operation/write/insert_spec.rb +1 -1
  154. data/spec/mongo/operation/write/update_spec.rb +1 -1
  155. data/spec/mongo/protocol/compressed_spec.rb +66 -0
  156. data/spec/mongo/protocol/delete_spec.rb +14 -0
  157. data/spec/mongo/protocol/get_more_spec.rb +14 -0
  158. data/spec/mongo/protocol/insert_spec.rb +14 -0
  159. data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
  160. data/spec/mongo/protocol/msg_spec.rb +499 -0
  161. data/spec/mongo/protocol/query_spec.rb +45 -0
  162. data/spec/mongo/protocol/registry_spec.rb +31 -0
  163. data/spec/mongo/protocol/reply_spec.rb +14 -0
  164. data/spec/mongo/protocol/update_spec.rb +14 -0
  165. data/spec/mongo/retryable_spec.rb +6 -2
  166. data/spec/mongo/sdam_spec.rb +4 -0
  167. data/spec/mongo/server/connection_spec.rb +4 -2
  168. data/spec/mongo/server/description_spec.rb +28 -1
  169. data/spec/mongo/session/server_session_spec.rb +16 -0
  170. data/spec/mongo/session/session_pool_spec.rb +194 -0
  171. data/spec/mongo/uri_spec.rb +31 -2
  172. data/spec/spec_helper.rb +104 -0
  173. data/spec/support/authorization.rb +6 -1
  174. data/spec/support/crud.rb +3 -1
  175. data/spec/support/crud/write.rb +6 -1
  176. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
  177. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
  178. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
  179. data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
  180. data/spec/support/sdam/rs/discover_passives.yml +2 -2
  181. data/spec/support/sdam/rs/discover_primary.yml +1 -1
  182. data/spec/support/sdam/rs/discover_secondary.yml +1 -1
  183. data/spec/support/sdam/rs/discovery.yml +4 -4
  184. data/spec/support/sdam/rs/equal_electionids.yml +1 -0
  185. data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
  186. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
  187. data/spec/support/sdam/rs/ls_timeout.yml +88 -0
  188. data/spec/support/sdam/rs/member_reconfig.yml +2 -2
  189. data/spec/support/sdam/rs/member_standalone.yml +2 -2
  190. data/spec/support/sdam/rs/new_primary.yml +2 -2
  191. data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
  192. data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
  193. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
  194. data/spec/support/sdam/rs/non_rs_member.yml +1 -1
  195. data/spec/support/sdam/rs/normalize_case.yml +1 -1
  196. data/spec/support/sdam/rs/null_election_id.yml +4 -0
  197. data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
  198. data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
  199. data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
  200. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
  201. data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
  202. data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
  203. data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
  204. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
  205. data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
  206. data/spec/support/sdam/rs/response_from_removed.yml +2 -2
  207. data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
  208. data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
  209. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
  210. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
  211. data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
  212. data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
  213. data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
  214. data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
  215. data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
  216. data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
  217. data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
  218. data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
  219. data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
  220. data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  225. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  226. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  227. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  228. data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
  229. data/spec/support/sdam/single/not_ok_response.yml +1 -1
  230. data/spec/support/sdam/single/standalone_removed.yml +1 -1
  231. data/spec/support/sdam/single/unavailable_seed.yml +1 -1
  232. data/spec/support/server_discovery_and_monitoring.rb +4 -0
  233. data/spec/support/shared/session.rb +236 -0
  234. metadata +53 -15
  235. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d433224b22b9fef9861385a7aa3bb1152c688e3
4
- data.tar.gz: c238f0884125f4ec1b3a70b3f051fd0ad61e6d51
3
+ metadata.gz: c4e52038faa9ef6ff0419d1c5855800d4ce07066
4
+ data.tar.gz: a7b342d735714530107b7b513ee91c8d071472ba
5
5
  SHA512:
6
- metadata.gz: 898d76cdc7e59033be68aabae3f8591397577e8d15286de25f07327803c42581febbf40a874303766fdc204f5d617523e46f46e663e9b5d8567b34f96a87fd96
7
- data.tar.gz: 71d59dd4c289346aedf2bf9e9ad0161f4a98830ee70cadc9d6c45be47eda5c84bc41bde1d261d2d53230fd82350c2bc2d16409656da84e5faaab23660b4190ed
6
+ metadata.gz: eb58160cfaa1676ee31df0fcd375e2cad12953b46ba063b8a5f46c6e9dcbdcbf750a5c299e8b4222b3af57c9a0902c9f64fe4dfc33dc37df689730cf19f88e27
7
+ data.tar.gz: 97cc226f8bc3fc8fd7bda2d1be3b29be4a8d03f1e444b572dc74008e1276f733a3408851132cef3241df936c4e1613e4bce2ef2b3597e2fb55031ccfed49fb4c
checksums.yaml.gz.sig CHANGED
@@ -1,2 +1,3 @@
1
- �~�� ���tO���x����)�e[��t
2
- 2�R�g)>1ĕ�ŗwm
1
+ �Ca�? @�7��G�I�ͥ'
2
+ mOF/�
3
+ Z�/(詏�v-�v���d�A�e.R%-Ş�O@Y̽͛�W㞧��ȁC�&�E��>qa�*!~7�>�?[�P�k�H��͍���N�:Yg�S�3ͭ�yf�!���0�|�sd�jٯ�z��"�'J7e��V�i¥~V���+J�<AOs�o���m�!�b���([�|k'���� �#jݱr���@�C<MTAv kA旆Ԧ^�f�ӛ��Ñ�� wX���dx
data.tar.gz.sig CHANGED
Binary file
data/lib/mongo.rb CHANGED
@@ -26,17 +26,18 @@ require 'mongo/error'
26
26
  require 'mongo/event'
27
27
  require 'mongo/address'
28
28
  require 'mongo/auth'
29
+ require 'mongo/protocol'
29
30
  require 'mongo/client'
30
31
  require 'mongo/cluster'
31
- require 'mongo/collection'
32
32
  require 'mongo/cursor'
33
+ require 'mongo/collection'
33
34
  require 'mongo/database'
34
35
  require 'mongo/dbref'
35
36
  require 'mongo/grid'
36
37
  require 'mongo/index'
37
- require 'mongo/protocol'
38
38
  require 'mongo/server'
39
39
  require 'mongo/server_selector'
40
+ require 'mongo/session'
40
41
  require 'mongo/socket'
41
42
  require 'mongo/uri'
42
43
  require 'mongo/version'
data/lib/mongo/auth/cr.rb CHANGED
@@ -49,14 +49,16 @@ module Mongo
49
49
  #
50
50
  # @param [ Mongo::Connection ] connection The connection to log into.
51
51
  #
52
- # @return [ Protocol::Reply ] The authentication response.
52
+ # @return [ Protocol::Message ] The authentication response.
53
53
  #
54
54
  # @since 2.0.0
55
55
  def login(connection)
56
56
  conversation = Conversation.new(user)
57
- reply = connection.dispatch([ conversation.start ])
58
- reply = connection.dispatch([ conversation.continue(reply) ])
59
- conversation.finalize(reply)
57
+ reply = connection.dispatch([ conversation.start(connection) ])
58
+ connection.update_cluster_time(Operation::Result.new(reply))
59
+ reply = connection.dispatch([ conversation.continue(reply, connection) ])
60
+ connection.update_cluster_time(Operation::Result.new(reply))
61
+ conversation.finalize(reply, connection)
60
62
  end
61
63
  end
62
64
  end
@@ -27,7 +27,7 @@ module Mongo
27
27
  # @since 2.0.0
28
28
  LOGIN = { authenticate: 1 }.freeze
29
29
 
30
- # @return [ Protocol::Reply ] reply The current reply in the
30
+ # @return [ Protocol::Message ] reply The current reply in the
31
31
  # conversation.
32
32
  attr_reader :reply
33
33
 
@@ -47,20 +47,29 @@ module Mongo
47
47
  # @example Continue the conversation.
48
48
  # conversation.continue(reply)
49
49
  #
50
- # @param [ Protocol::Reply ] reply The reply of the previous
50
+ # @param [ Protocol::Message ] reply The reply of the previous
51
51
  # message.
52
+ # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
52
53
  #
53
54
  # @return [ Protocol::Query ] The next message to send.
54
55
  #
55
56
  # @since 2.0.0
56
- def continue(reply)
57
+ def continue(reply, connection = nil)
57
58
  validate!(reply)
58
- Protocol::Query.new(
59
- user.auth_source,
60
- Database::COMMAND,
61
- LOGIN.merge(user: user.name, nonce: nonce, key: user.auth_key(nonce)),
62
- limit: -1
63
- )
59
+ if connection && connection.features.op_msg_enabled?
60
+ selector = LOGIN.merge(user: user.name, nonce: nonce, key: user.auth_key(nonce))
61
+ selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
62
+ cluster_time = connection.mongos? && connection.cluster_time
63
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
64
+ Protocol::Msg.new([:none], {}, selector)
65
+ else
66
+ Protocol::Query.new(
67
+ user.auth_source,
68
+ Database::COMMAND,
69
+ LOGIN.merge(user: user.name, nonce: nonce, key: user.auth_key(nonce)),
70
+ limit: -1
71
+ )
72
+ end
64
73
  end
65
74
 
66
75
  # Finalize the CR conversation. This is meant to be iterated until
@@ -69,13 +78,13 @@ module Mongo
69
78
  # @example Finalize the conversation.
70
79
  # conversation.finalize(reply)
71
80
  #
72
- # @param [ Protocol::Reply ] reply The reply of the previous
81
+ # @param [ Protocol::Message ] reply The reply of the previous
73
82
  # message.
74
83
  #
75
84
  # @return [ Protocol::Query ] The next message to send.
76
85
  #
77
86
  # @since 2.0.0
78
- def finalize(reply)
87
+ def finalize(reply, connection = nil)
79
88
  validate!(reply)
80
89
  end
81
90
 
@@ -88,12 +97,19 @@ module Mongo
88
97
  # @return [ Protocol::Query ] The first CR conversation message.
89
98
  #
90
99
  # @since 2.0.0
91
- def start
92
- Protocol::Query.new(
93
- user.auth_source,
94
- Database::COMMAND,
95
- Auth::GET_NONCE,
96
- limit: -1)
100
+ def start(connection = nil)
101
+ if connection && connection.features.op_msg_enabled?
102
+ selector = Auth::GET_NONCE.merge(Protocol::Msg::DATABASE_IDENTIFIER => user.auth_source)
103
+ cluster_time = connection.mongos? && connection.cluster_time
104
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
105
+ Protocol::Msg.new([:none], {}, selector)
106
+ else
107
+ Protocol::Query.new(
108
+ user.auth_source,
109
+ Database::COMMAND,
110
+ Auth::GET_NONCE,
111
+ limit: -1)
112
+ end
97
113
  end
98
114
 
99
115
  # Create the new conversation.
@@ -49,12 +49,14 @@ module Mongo
49
49
  # @param [ Mongo::Connection ] connection The connection to log into.
50
50
  # on.
51
51
  #
52
- # @return [ Protocol::Reply ] The authentication response.
52
+ # @return [ Protocol::Message ] The authentication response.
53
53
  #
54
54
  # @since 2.0.0
55
55
  def login(connection)
56
56
  conversation = Conversation.new(user)
57
- conversation.finalize(connection.dispatch([ conversation.start ]))
57
+ reply = connection.dispatch([ conversation.start(connection) ])
58
+ connection.update_cluster_time(Operation::Result.new(reply))
59
+ conversation.finalize(reply)
58
60
  end
59
61
  end
60
62
  end
@@ -27,7 +27,7 @@ module Mongo
27
27
  # @since 2.0.0
28
28
  LOGIN = { saslStart: 1, autoAuthorize: 1 }.freeze
29
29
 
30
- # @return [ Protocol::Reply ] reply The current reply in the
30
+ # @return [ Protocol::Message ] reply The current reply in the
31
31
  # conversation.
32
32
  attr_reader :reply
33
33
 
@@ -40,7 +40,7 @@ module Mongo
40
40
  # @example Finalize the conversation.
41
41
  # conversation.finalize(reply)
42
42
  #
43
- # @param [ Protocol::Reply ] reply The reply of the previous
43
+ # @param [ Protocol::Message ] reply The reply of the previous
44
44
  # message.
45
45
  #
46
46
  # @return [ Protocol::Query ] The next message to send.
@@ -56,16 +56,26 @@ module Mongo
56
56
  # @example Start the conversation.
57
57
  # conversation.start
58
58
  #
59
+ # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
60
+ #
59
61
  # @return [ Protocol::Query ] The first PLAIN conversation message.
60
62
  #
61
63
  # @since 2.0.0
62
- def start
63
- Protocol::Query.new(
64
- Auth::EXTERNAL,
65
- Database::COMMAND,
66
- LOGIN.merge(payload: payload, mechanism: LDAP::MECHANISM),
67
- limit: -1
68
- )
64
+ def start(connection = nil)
65
+ if connection && connection.features.op_msg_enabled?
66
+ selector = LOGIN.merge(payload: payload, mechanism: LDAP::MECHANISM)
67
+ selector[Protocol::Msg::DATABASE_IDENTIFIER] = Auth::EXTERNAL
68
+ cluster_time = connection.mongos? && connection.cluster_time
69
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
70
+ Protocol::Msg.new([:none], {}, selector)
71
+ else
72
+ Protocol::Query.new(
73
+ Auth::EXTERNAL,
74
+ Database::COMMAND,
75
+ LOGIN.merge(payload: payload, mechanism: LDAP::MECHANISM),
76
+ limit: -1
77
+ )
78
+ end
69
79
  end
70
80
 
71
81
  # Create the new conversation.
@@ -50,15 +50,18 @@ module Mongo
50
50
  # @param [ Mongo::Connection ] connection The connection to log into.
51
51
  # on.
52
52
  #
53
- # @return [ Protocol::Reply ] The authentication response.
53
+ # @return [ Protocol::Message ] The authentication response.
54
54
  #
55
55
  # @since 2.0.0
56
56
  def login(connection)
57
57
  conversation = Conversation.new(user)
58
- reply = connection.dispatch([ conversation.start ])
59
- reply = connection.dispatch([ conversation.continue(reply) ])
58
+ reply = connection.dispatch([ conversation.start(connection) ])
59
+ connection.update_cluster_time(Operation::Result.new(reply))
60
+ reply = connection.dispatch([ conversation.continue(reply, connection) ])
61
+ connection.update_cluster_time(Operation::Result.new(reply))
60
62
  until reply.documents[0][Conversation::DONE]
61
- reply = connection.dispatch([ conversation.finalize(reply) ])
63
+ reply = connection.dispatch([ conversation.finalize(reply, connection) ])
64
+ connection.update_cluster_time(Operation::Result.new(reply))
62
65
  end
63
66
  reply
64
67
  end
@@ -88,7 +88,7 @@ module Mongo
88
88
  # @return [ String ] nonce The initial user nonce.
89
89
  attr_reader :nonce
90
90
 
91
- # @return [ Protocol::Reply ] reply The current reply in the
91
+ # @return [ Protocol::Message ] reply The current reply in the
92
92
  # conversation.
93
93
  attr_reader :reply
94
94
 
@@ -102,20 +102,36 @@ module Mongo
102
102
  # @example Continue the conversation.
103
103
  # conversation.continue(reply)
104
104
  #
105
- # @param [ Protocol::Reply ] reply The reply of the previous
105
+ # @param [ Protocol::Message ] reply The reply of the previous
106
106
  # message.
107
+ # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
107
108
  #
108
109
  # @return [ Protocol::Query ] The next message to send.
109
110
  #
110
111
  # @since 2.0.0
111
- def continue(reply)
112
+ def continue(reply, connection = nil)
112
113
  validate_first_message!(reply)
113
- Protocol::Query.new(
114
- user.auth_source,
115
- Database::COMMAND,
116
- CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id),
117
- limit: -1
118
- )
114
+
115
+ # The salted password needs to be calculated now; otherwise, if the
116
+ # client key is cached from a previous authentication, the salt in the
117
+ # reply will no longer be available for when the salted password is
118
+ # needed to calculate the server key.
119
+ salted_password
120
+
121
+ if connection && connection.features.op_msg_enabled?
122
+ selector = CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id)
123
+ selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
124
+ cluster_time = connection.mongos? && connection.cluster_time
125
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
126
+ Protocol::Msg.new([:none], {}, selector)
127
+ else
128
+ Protocol::Query.new(
129
+ user.auth_source,
130
+ Database::COMMAND,
131
+ CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id),
132
+ limit: -1
133
+ )
134
+ end
119
135
  end
120
136
 
121
137
  # Finalize the SCRAM conversation. This is meant to be iterated until
@@ -124,20 +140,29 @@ module Mongo
124
140
  # @example Finalize the conversation.
125
141
  # conversation.finalize(reply)
126
142
  #
127
- # @param [ Protocol::Reply ] reply The reply of the previous
143
+ # @param [ Protocol::Message ] reply The reply of the previous
128
144
  # message.
145
+ # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
129
146
  #
130
147
  # @return [ Protocol::Query ] The next message to send.
131
148
  #
132
149
  # @since 2.0.0
133
- def finalize(reply)
150
+ def finalize(reply, connection = nil)
134
151
  validate_final_message!(reply)
135
- Protocol::Query.new(
136
- user.auth_source,
137
- Database::COMMAND,
138
- CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id),
139
- limit: -1
140
- )
152
+ if connection && connection.features.op_msg_enabled?
153
+ selector = CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id)
154
+ selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
155
+ cluster_time = connection.mongos? && connection.cluster_time
156
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
157
+ Protocol::Msg.new([:none], {}, selector)
158
+ else
159
+ Protocol::Query.new(
160
+ user.auth_source,
161
+ Database::COMMAND,
162
+ CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id),
163
+ limit: -1
164
+ )
165
+ end
141
166
  end
142
167
 
143
168
  # Start the SCRAM conversation. This returns the first message that
@@ -146,16 +171,26 @@ module Mongo
146
171
  # @example Start the conversation.
147
172
  # conversation.start
148
173
  #
174
+ # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
175
+ #
149
176
  # @return [ Protocol::Query ] The first SCRAM conversation message.
150
177
  #
151
178
  # @since 2.0.0
152
- def start
153
- Protocol::Query.new(
154
- user.auth_source,
155
- Database::COMMAND,
156
- CLIENT_FIRST_MESSAGE.merge(payload: client_first_message, mechanism: SCRAM::MECHANISM),
157
- limit: -1
158
- )
179
+ def start(connection = nil)
180
+ if connection && connection.features.op_msg_enabled?
181
+ selector = CLIENT_FIRST_MESSAGE.merge(payload: client_first_message, mechanism: SCRAM::MECHANISM)
182
+ selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
183
+ cluster_time = connection.mongos? && connection.cluster_time
184
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
185
+ Protocol::Msg.new([:none], {}, selector)
186
+ else
187
+ Protocol::Query.new(
188
+ user.auth_source,
189
+ Database::COMMAND,
190
+ CLIENT_FIRST_MESSAGE.merge(payload: client_first_message, mechanism: SCRAM::MECHANISM),
191
+ limit: -1
192
+ )
193
+ end
159
194
  end
160
195
 
161
196
  # Get the id of the conversation.
@@ -181,6 +216,7 @@ module Mongo
181
216
  def initialize(user)
182
217
  @user = user
183
218
  @nonce = SecureRandom.base64
219
+ @client_key = user.send(:client_key)
184
220
  end
185
221
 
186
222
  private
@@ -247,6 +283,8 @@ module Mongo
247
283
  # @since 2.0.0
248
284
  def client_key
249
285
  @client_key ||= hmac(salted_password, CLIENT_KEY)
286
+ user.instance_variable_set(:@client_key, @client_key) unless user.send(:client_key)
287
+ @client_key
250
288
  end
251
289
 
252
290
  # Client proof algorithm implementation.
@@ -131,6 +131,8 @@ module Mongo
131
131
  # @option options [ String ] :password The user's password.
132
132
  # @option options [ Symbol ] :auth_mech The authorization mechanism.
133
133
  # @option options [ Array<String>, Array<Hash> ] roles The user roles.
134
+ # @option options [ String ] :client_key The user's client key cached from a previous
135
+ # authentication on the same connection.
134
136
  #
135
137
  # @since 2.0.0
136
138
  def initialize(options)
@@ -141,6 +143,7 @@ module Mongo
141
143
  @mechanism = options[:auth_mech] || :mongodb_cr
142
144
  @auth_mech_properties = options[:auth_mech_properties] || {}
143
145
  @roles = options[:roles] || []
146
+ @client_key = options[:client_key]
144
147
  end
145
148
 
146
149
  # Get the specification for the user, used in creation.
@@ -154,6 +157,13 @@ module Mongo
154
157
  def spec
155
158
  { pwd: hashed_password, roles: roles }
156
159
  end
160
+
161
+ private
162
+
163
+ # The client key for the user.
164
+ #
165
+ # @return [ String ] The client key for the user.
166
+ attr_reader :client_key
157
167
  end
158
168
  end
159
169
  end
@@ -25,7 +25,7 @@ module Mongo
25
25
  # @return [ Database ] database The view's database.
26
26
  attr_reader :database
27
27
 
28
- def_delegators :database, :cluster, :read_preference
28
+ def_delegators :database, :cluster, :read_preference, :client
29
29
  def_delegators :cluster, :next_primary
30
30
 
31
31
  # Create a new user in the database.
@@ -36,15 +36,20 @@ module Mongo
36
36
  # @param [ Auth::User, String ] user_or_name The user object or user name.
37
37
  # @param [ Hash ] options The user options.
38
38
  #
39
+ # @option options [ Session ] :session The session to use for the operation.
40
+ #
39
41
  # @return [ Result ] The command response.
40
42
  #
41
43
  # @since 2.0.0
42
44
  def create(user_or_name, options = {})
43
45
  user = generate(user_or_name, options)
44
- Operation::Write::CreateUser.new(
45
- user: user,
46
- db_name: database.name
47
- ).execute(next_primary)
46
+ client.send(:with_session, options) do |session|
47
+ Operation::Write::CreateUser.new(
48
+ user: user,
49
+ db_name: database.name,
50
+ session: session
51
+ ).execute(next_primary)
52
+ end
48
53
  end
49
54
 
50
55
  # Initialize the new user view.
@@ -65,15 +70,21 @@ module Mongo
65
70
  # view.remove('user')
66
71
  #
67
72
  # @param [ String ] name The user name.
73
+ # @param [ Hash ] options The options for the remove operation.
74
+ #
75
+ # @option options [ Session ] :session The session to use for the operation.
68
76
  #
69
77
  # @return [ Result ] The command response.
70
78
  #
71
79
  # @since 2.0.0
72
- def remove(name)
73
- Operation::Write::RemoveUser.new(
74
- user_name: name,
75
- db_name: database.name
76
- ).execute(next_primary)
80
+ def remove(name, options = {})
81
+ client.send(:with_session, options) do |session|
82
+ Operation::Write::RemoveUser.new(
83
+ user_name: name,
84
+ db_name: database.name,
85
+ session: session
86
+ ).execute(next_primary)
87
+ end
77
88
  end
78
89
 
79
90
  # Update a user in the database.
@@ -84,15 +95,20 @@ module Mongo
84
95
  # @param [ Auth::User, String ] user_or_name The user object or user name.
85
96
  # @param [ Hash ] options The user options.
86
97
  #
98
+ # @option options [ Session ] :session The session to use for the operation.
99
+ #
87
100
  # @return [ Result ] The response.
88
101
  #
89
102
  # @since 2.0.0
90
103
  def update(user_or_name, options = {})
91
- user = generate(user_or_name, options)
92
- Operation::Write::UpdateUser.new(
93
- user: user,
94
- db_name: database.name
95
- ).execute(next_primary)
104
+ client.send(:with_session, options) do |session|
105
+ user = generate(user_or_name, options)
106
+ Operation::Write::UpdateUser.new(
107
+ user: user,
108
+ db_name: database.name,
109
+ session: session
110
+ ).execute(next_primary)
111
+ end
96
112
  end
97
113
 
98
114
  # Get info for a particular user in the database.
@@ -101,21 +117,27 @@ module Mongo
101
117
  # view.info('emily')
102
118
  #
103
119
  # @param [ String ] name The user name.
120
+ # @param [ Hash ] options The options for the info operation.
121
+ #
122
+ # @option options [ Session ] :session The session to use for the operation.
104
123
  #
105
124
  # @return [ Hash ] A document containing information on a particular user.
106
125
  #
107
126
  # @since 2.1.0
108
- def info(name)
109
- user_query(name).documents
127
+ def info(name, options = {})
128
+ user_query(name, options).documents
110
129
  end
111
130
 
112
131
  private
113
132
 
114
- def user_query(name)
115
- Operation::Commands::UserQuery.new(
116
- user_name: name,
117
- db_name: database.name
118
- ).execute(next_primary)
133
+ def user_query(name, options = {})
134
+ client.send(:with_session, options) do |session|
135
+ Operation::Commands::UserQuery.new(
136
+ user_name: name,
137
+ db_name: database.name,
138
+ session: session
139
+ ).execute(next_primary)
140
+ end
119
141
  end
120
142
 
121
143
  def generate(user, options)