mongo 2.4.3 → 2.5.0.beta

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 (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)