mongo 2.24.0 → 2.25.0
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.
- checksums.yaml +4 -4
- data/bin/mongo_console +2 -2
- data/lib/mongo/address/validator.rb +1 -1
- data/lib/mongo/address.rb +1 -3
- data/lib/mongo/auth/aws/conversation.rb +0 -4
- data/lib/mongo/auth/aws/credentials_retriever.rb +3 -8
- data/lib/mongo/auth/base.rb +1 -1
- data/lib/mongo/auth/sasl_conversation_base.rb +1 -1
- data/lib/mongo/auth/scram_conversation_base.rb +1 -8
- data/lib/mongo/auth/stringprep.rb +1 -6
- data/lib/mongo/auth/user/view.rb +1 -1
- data/lib/mongo/bulk_write/result.rb +10 -2
- data/lib/mongo/bulk_write/result_combiner.rb +11 -4
- data/lib/mongo/bulk_write.rb +1 -1
- data/lib/mongo/client.rb +82 -12
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
- data/lib/mongo/cluster/sdam_flow.rb +4 -4
- data/lib/mongo/cluster/topology/base.rb +1 -1
- data/lib/mongo/cluster.rb +12 -28
- data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +2 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +14 -9
- data/lib/mongo/collection/view/map_reduce.rb +2 -11
- data/lib/mongo/collection/view/readable.rb +11 -7
- data/lib/mongo/collection/view/writable.rb +4 -4
- data/lib/mongo/collection/view.rb +8 -4
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/config.rb +5 -0
- data/lib/mongo/crypt/auto_encrypter.rb +1 -1
- data/lib/mongo/crypt/binary.rb +7 -4
- data/lib/mongo/crypt/binding.rb +88 -10
- data/lib/mongo/crypt/context.rb +50 -19
- data/lib/mongo/crypt/encryption_io.rb +11 -5
- data/lib/mongo/crypt/handle.rb +17 -5
- data/lib/mongo/crypt/kms/credentials.rb +70 -31
- data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
- data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
- data/lib/mongo/crypt/kms.rb +12 -1
- data/lib/mongo/cursor.rb +8 -2
- data/lib/mongo/database/cursor_command_view.rb +95 -0
- data/lib/mongo/database.rb +144 -18
- data/lib/mongo/error/bulk_write_error.rb +29 -1
- data/lib/mongo/error/invalid_server_preference.rb +3 -0
- data/lib/mongo/error/invalid_uri.rb +5 -1
- data/lib/mongo/error/operation_failure.rb +42 -3
- data/lib/mongo/error/parser.rb +3 -3
- data/lib/mongo/error/sdam_error_detection.rb +2 -3
- data/lib/mongo/grid/fs_bucket.rb +27 -21
- data/lib/mongo/grid/stream/write.rb +2 -2
- data/lib/mongo/index/view.rb +13 -7
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
- data/lib/mongo/monitoring/event/command_failed.rb +1 -0
- data/lib/mongo/monitoring/event/command_started.rb +1 -0
- data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/secure.rb +12 -17
- data/lib/mongo/monitoring/event/server_closed.rb +1 -0
- data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/server_opening.rb +1 -0
- data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
- data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +2 -0
- data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +3 -0
- data/lib/mongo/operation/create/op_msg.rb +1 -0
- data/lib/mongo/operation/create_index/op_msg.rb +2 -1
- data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
- data/lib/mongo/operation/cursor_command/result.rb +60 -0
- data/lib/mongo/operation/cursor_command.rb +33 -0
- data/lib/mongo/operation/delete/op_msg.rb +1 -0
- data/lib/mongo/operation/drop/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
- data/lib/mongo/operation/find/builder/command.rb +1 -1
- data/lib/mongo/operation/insert/bulk_result.rb +2 -4
- data/lib/mongo/operation/insert/op_msg.rb +1 -0
- data/lib/mongo/operation/result.rb +14 -26
- data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
- data/lib/mongo/operation/shared/executable.rb +2 -10
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
- data/lib/mongo/operation/update/bulk_result.rb +1 -1
- data/lib/mongo/operation/update/op_msg.rb +1 -0
- data/lib/mongo/operation/write_command/op_msg.rb +2 -0
- data/lib/mongo/operation.rb +1 -0
- data/lib/mongo/options/redacted.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +15 -11
- data/lib/mongo/protocol/message.rb +16 -16
- data/lib/mongo/protocol/registry.rb +1 -1
- data/lib/mongo/protocol/serializers.rb +1 -3
- data/lib/mongo/query_cache.rb +3 -5
- data/lib/mongo/server/connection.rb +3 -1
- data/lib/mongo/server/connection_common.rb +9 -1
- data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
- data/lib/mongo/server/connection_pool.rb +79 -18
- data/lib/mongo/server/description/features.rb +2 -2
- data/lib/mongo/server/description.rb +24 -2
- data/lib/mongo/server/monitor/connection.rb +3 -2
- data/lib/mongo/server/monitor.rb +133 -30
- data/lib/mongo/server/pending_connection.rb +1 -0
- data/lib/mongo/server/push_monitor.rb +16 -2
- data/lib/mongo/server.rb +27 -7
- data/lib/mongo/server_selector/base.rb +16 -6
- data/lib/mongo/server_selector/nearest.rb +3 -0
- data/lib/mongo/server_selector/primary.rb +3 -0
- data/lib/mongo/server_selector/primary_preferred.rb +3 -0
- data/lib/mongo/server_selector/secondary.rb +3 -0
- data/lib/mongo/server_selector/secondary_preferred.rb +7 -1
- data/lib/mongo/session.rb +56 -24
- data/lib/mongo/socket/ssl.rb +3 -3
- data/lib/mongo/socket.rb +1 -7
- data/lib/mongo/srv/monitor.rb +11 -2
- data/lib/mongo/srv/resolver.rb +1 -1
- data/lib/mongo/srv/result.rb +5 -3
- data/lib/mongo/timeout.rb +10 -15
- data/lib/mongo/tracing/open_telemetry/command_tracer.rb +43 -2
- data/lib/mongo/uri/options_mapper.rb +21 -9
- data/lib/mongo/uri/srv_protocol.rb +4 -4
- data/lib/mongo/uri.rb +56 -8
- data/lib/mongo/utils.rb +1 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +1 -0
- metadata +6 -4
- data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
- data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +0 -1164
|
@@ -351,19 +351,24 @@ module Mongo
|
|
|
351
351
|
# In load balanced topology, manually check out a connection
|
|
352
352
|
# so it remains checked out and pinned to the cursor.
|
|
353
353
|
connection = server.pool.check_out(context: context)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
354
|
+
begin
|
|
355
|
+
result = send_initial_query(connection, context)
|
|
356
|
+
|
|
357
|
+
start_at_operation_time = if (doc = result.replies.first && result.replies.first.documents.first)
|
|
358
|
+
doc['operationTime']
|
|
359
|
+
else
|
|
360
|
+
nil
|
|
361
|
+
end
|
|
362
|
+
result
|
|
363
|
+
rescue StandardError
|
|
364
|
+
server.pool.check_in(connection)
|
|
365
|
+
raise
|
|
366
|
+
end
|
|
362
367
|
else
|
|
363
368
|
server.with_connection do |connection|
|
|
364
369
|
result = send_initial_query(connection, context)
|
|
365
370
|
|
|
366
|
-
start_at_operation_time = if doc = result.replies.first && result.replies.first.documents.first
|
|
371
|
+
start_at_operation_time = if (doc = result.replies.first && result.replies.first.documents.first)
|
|
367
372
|
doc['operationTime']
|
|
368
373
|
else
|
|
369
374
|
# The above may set @start_at_operation_time to nil
|
|
@@ -227,7 +227,7 @@ module Mongo
|
|
|
227
227
|
#
|
|
228
228
|
# @since 2.5.0
|
|
229
229
|
def execute
|
|
230
|
-
view.
|
|
230
|
+
view.with_session(@options) do |session|
|
|
231
231
|
write_concern = view.write_concern_with_session(session)
|
|
232
232
|
context = Operation::Context.new(client: client, session: session)
|
|
233
233
|
nro_write_with_retry(write_concern, context: context) do |connection, _txn_num, context|
|
|
@@ -241,7 +241,7 @@ module Mongo
|
|
|
241
241
|
OUT_ACTIONS = %i[replace merge reduce].freeze
|
|
242
242
|
|
|
243
243
|
def server_selector
|
|
244
|
-
@view.
|
|
244
|
+
@view.server_selector
|
|
245
245
|
end
|
|
246
246
|
|
|
247
247
|
def inline?
|
|
@@ -299,15 +299,6 @@ module Mongo
|
|
|
299
299
|
end
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
-
def fetch_query_spec
|
|
303
|
-
Builder::MapReduce.new(map_function, reduce_function, view, options).query_specification
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
def find_command_spec(session)
|
|
307
|
-
Builder::MapReduce.new(map_function, reduce_function, view,
|
|
308
|
-
options.merge(session: session)).command_specification
|
|
309
|
-
end
|
|
310
|
-
|
|
311
302
|
def fetch_query_op(session)
|
|
312
303
|
spec = {
|
|
313
304
|
coll_name: out_collection_name,
|
|
@@ -678,7 +678,7 @@ module Mongo
|
|
|
678
678
|
# @api private
|
|
679
679
|
def read_concern
|
|
680
680
|
if options[:session] && options[:session].in_transaction?
|
|
681
|
-
options[:session].
|
|
681
|
+
options[:session].txn_read_concern || collection.client.read_concern
|
|
682
682
|
else
|
|
683
683
|
collection.read_concern
|
|
684
684
|
end
|
|
@@ -749,12 +749,10 @@ module Mongo
|
|
|
749
749
|
end
|
|
750
750
|
end
|
|
751
751
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
end
|
|
757
|
-
|
|
752
|
+
# The server selector for this view, derived from its read
|
|
753
|
+
# preference (or the collection/client default).
|
|
754
|
+
#
|
|
755
|
+
# @api private
|
|
758
756
|
def server_selector
|
|
759
757
|
@server_selector ||= if options[:session] && options[:session].in_transaction?
|
|
760
758
|
ServerSelector.get(read_preference || client.server_selector)
|
|
@@ -763,6 +761,12 @@ module Mongo
|
|
|
763
761
|
end
|
|
764
762
|
end
|
|
765
763
|
|
|
764
|
+
private
|
|
765
|
+
|
|
766
|
+
def collation(doc = nil)
|
|
767
|
+
configure(:collation, doc)
|
|
768
|
+
end
|
|
769
|
+
|
|
766
770
|
def validate_doc!(doc)
|
|
767
771
|
raise Error::InvalidDocument.new unless doc.respond_to?(:keys)
|
|
768
772
|
end
|
|
@@ -624,8 +624,8 @@ module Mongo
|
|
|
624
624
|
# @raise [ Error::InvalidUpdateDocument ] if the first key in the
|
|
625
625
|
# document does not start with a $.
|
|
626
626
|
def validate_update_documents!(spec)
|
|
627
|
-
return unless update = spec.is_a?(Array) ? spec&.first : spec
|
|
628
|
-
return unless key = update.keys&.first
|
|
627
|
+
return unless (update = spec.is_a?(Array) ? spec&.first : spec)
|
|
628
|
+
return unless (key = update.keys&.first)
|
|
629
629
|
return if key.to_s.start_with?('$')
|
|
630
630
|
raise Error::InvalidUpdateDocument.new(key: key) if Mongo.validate_update_replace
|
|
631
631
|
|
|
@@ -641,8 +641,8 @@ module Mongo
|
|
|
641
641
|
# @raise [ Error::InvalidUpdateDocument ] if the first key in the
|
|
642
642
|
# document does not start with a $.
|
|
643
643
|
def validate_replacement_documents!(spec)
|
|
644
|
-
return unless replace = spec.is_a?(Array) ? spec&.first : spec
|
|
645
|
-
return unless key = replace.keys&.first
|
|
644
|
+
return unless (replace = spec.is_a?(Array) ? spec&.first : spec)
|
|
645
|
+
return unless (key = replace.keys&.first)
|
|
646
646
|
return unless key.to_s.start_with?('$')
|
|
647
647
|
raise Error::InvalidReplacementDocument.new(key: key) if Mongo.validate_update_replace
|
|
648
648
|
|
|
@@ -236,6 +236,14 @@ module Mongo
|
|
|
236
236
|
end
|
|
237
237
|
end
|
|
238
238
|
|
|
239
|
+
# Executes the provided block within the context of a session, using
|
|
240
|
+
# this view's options merged with the given ones.
|
|
241
|
+
#
|
|
242
|
+
# @api private
|
|
243
|
+
def with_session(opts = {}, &block)
|
|
244
|
+
client.with_session(@options.merge(opts), &block)
|
|
245
|
+
end
|
|
246
|
+
|
|
239
247
|
private
|
|
240
248
|
|
|
241
249
|
def initialize_copy(other)
|
|
@@ -252,10 +260,6 @@ module Mongo
|
|
|
252
260
|
def view
|
|
253
261
|
self
|
|
254
262
|
end
|
|
255
|
-
|
|
256
|
-
def with_session(opts = {}, &block)
|
|
257
|
-
client.with_session(@options.merge(opts), &block)
|
|
258
|
-
end
|
|
259
263
|
end
|
|
260
264
|
end
|
|
261
265
|
end
|
data/lib/mongo/collection.rb
CHANGED
|
@@ -393,7 +393,7 @@ module Mongo
|
|
|
393
393
|
operation = { create: name }.merge(options)
|
|
394
394
|
operation.delete(:write)
|
|
395
395
|
operation.delete(:write_concern)
|
|
396
|
-
client.
|
|
396
|
+
client.with_session(opts) do |session|
|
|
397
397
|
write_concern = if opts[:write_concern]
|
|
398
398
|
WriteConcern.get(opts[:write_concern])
|
|
399
399
|
else
|
data/lib/mongo/config.rb
CHANGED
|
@@ -29,6 +29,11 @@ module Mongo
|
|
|
29
29
|
# decryption instead of BSON types.
|
|
30
30
|
option :csfle_convert_to_ruby_types, default: false
|
|
31
31
|
|
|
32
|
+
# When this flag is set to true, the (host:port) of the server that produced
|
|
33
|
+
# the error is appended to error messages for OperationFailure and
|
|
34
|
+
# BulkWriteError. See RUBY-3602.
|
|
35
|
+
option :include_server_address_in_errors, default: false
|
|
36
|
+
|
|
32
37
|
# Set the configuration options.
|
|
33
38
|
#
|
|
34
39
|
# @example Set the options.
|
data/lib/mongo/crypt/binary.rb
CHANGED
|
@@ -139,14 +139,17 @@ module Mongo
|
|
|
139
139
|
# Wraps a String with a mongocrypt_binary_t, yielding an FFI::Pointer
|
|
140
140
|
# to the wrapped struct.
|
|
141
141
|
def self.wrap_string(str)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
# mongocrypt_binary_new_from_data does not copy the buffer, so the
|
|
143
|
+
# MemoryPointer must outlive the mongocrypt_binary_t. Hold it in a
|
|
144
|
+
# local so it stays referenced for the whole method frame; otherwise
|
|
145
|
+
# GC can free the buffer while libmongocrypt still points into it.
|
|
146
|
+
data_p = FFI::MemoryPointer.from_string(str)
|
|
147
|
+
binary_p = Binding.mongocrypt_binary_new_from_data(data_p, str.bytesize)
|
|
146
148
|
begin
|
|
147
149
|
yield binary_p
|
|
148
150
|
ensure
|
|
149
151
|
Binding.mongocrypt_binary_destroy(binary_p)
|
|
152
|
+
data_p # rubocop:disable Lint/Void -- keep the buffer alive past destroy
|
|
150
153
|
end
|
|
151
154
|
end
|
|
152
155
|
end
|
data/lib/mongo/crypt/binding.rb
CHANGED
|
@@ -106,7 +106,7 @@ module Mongo
|
|
|
106
106
|
def self.parse_version(version)
|
|
107
107
|
Gem::Version.new(version)
|
|
108
108
|
rescue ArgumentError
|
|
109
|
-
match = version.match(/\A(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?(
|
|
109
|
+
match = version.match(/\A(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?(?:-[A-Za-z+\d]+)?\z/)
|
|
110
110
|
raise ArgumentError.new("Malformed version number string #{version}") if match.nil?
|
|
111
111
|
|
|
112
112
|
Gem::Version.new(
|
|
@@ -823,14 +823,15 @@ module Mongo
|
|
|
823
823
|
|
|
824
824
|
# An enum labeling different libmognocrypt state machine states
|
|
825
825
|
enum :mongocrypt_ctx_state, [
|
|
826
|
-
:error,
|
|
827
|
-
:need_mongo_collinfo,
|
|
828
|
-
:need_mongo_markings,
|
|
829
|
-
:need_mongo_keys,
|
|
830
|
-
:need_kms,
|
|
831
|
-
:ready,
|
|
832
|
-
:done,
|
|
833
|
-
:need_kms_credentials,
|
|
826
|
+
:error, 0,
|
|
827
|
+
:need_mongo_collinfo, 1,
|
|
828
|
+
:need_mongo_markings, 2,
|
|
829
|
+
:need_mongo_keys, 3,
|
|
830
|
+
:need_kms, 4,
|
|
831
|
+
:ready, 5,
|
|
832
|
+
:done, 6,
|
|
833
|
+
:need_kms_credentials, 7,
|
|
834
|
+
:need_mongo_collinfo_with_db, 8,
|
|
834
835
|
]
|
|
835
836
|
|
|
836
837
|
# @!method self.mongocrypt_ctx_state(ctx)
|
|
@@ -1105,10 +1106,20 @@ module Mongo
|
|
|
1105
1106
|
def self.check_kms_ctx_status(kms_context)
|
|
1106
1107
|
return if yield
|
|
1107
1108
|
|
|
1109
|
+
kms_ctx_status(kms_context).raise_crypt_error(kms: true)
|
|
1110
|
+
end
|
|
1111
|
+
|
|
1112
|
+
# Read the status information for the given KmsContext into a Status
|
|
1113
|
+
# object without raising.
|
|
1114
|
+
#
|
|
1115
|
+
# @param [ Mongo::Crypt::KmsContext ] kms_context
|
|
1116
|
+
#
|
|
1117
|
+
# @return [ Mongo::Crypt::Status ] The KMS context status.
|
|
1118
|
+
def self.kms_ctx_status(kms_context)
|
|
1108
1119
|
status = Status.new
|
|
1109
1120
|
|
|
1110
1121
|
mongocrypt_kms_ctx_status(kms_context.kms_ctx_p, status.ref)
|
|
1111
|
-
status
|
|
1122
|
+
status
|
|
1112
1123
|
end
|
|
1113
1124
|
|
|
1114
1125
|
# @!method self.mongocrypt_kms_ctx_usleep(ctx)
|
|
@@ -1646,6 +1657,73 @@ module Mongo
|
|
|
1646
1657
|
mongocrypt_setopt_use_need_kms_credentials_state(handle.ref)
|
|
1647
1658
|
end
|
|
1648
1659
|
|
|
1660
|
+
# @!method self.mongocrypt_setopt_enable_multiple_collinfo(crypt)
|
|
1661
|
+
# @api private
|
|
1662
|
+
#
|
|
1663
|
+
# Enable support for multiple collection schemas. Required to support $lookup.
|
|
1664
|
+
#
|
|
1665
|
+
# @param [ FFI::Pointer ] crypt A pointer to a mongocrypt_t object.
|
|
1666
|
+
# @pre mongocrypt_init has not been called on crypt.
|
|
1667
|
+
# @return [ Boolean ] Whether the option was set successfully.
|
|
1668
|
+
attach_function(
|
|
1669
|
+
:mongocrypt_setopt_enable_multiple_collinfo,
|
|
1670
|
+
[ :pointer ],
|
|
1671
|
+
:bool
|
|
1672
|
+
)
|
|
1673
|
+
|
|
1674
|
+
# Enable support for multiple collection schemas. Required to support $lookup.
|
|
1675
|
+
#
|
|
1676
|
+
# @param [ Mongo::Crypt::Handle ] handle
|
|
1677
|
+
# @return [ Boolean ] Whether the option was set successfully.
|
|
1678
|
+
def self.setopt_enable_multiple_collinfo(handle)
|
|
1679
|
+
check_status(handle) do
|
|
1680
|
+
mongocrypt_setopt_enable_multiple_collinfo(handle.ref)
|
|
1681
|
+
end
|
|
1682
|
+
end
|
|
1683
|
+
|
|
1684
|
+
# @!method self.mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(crypt)
|
|
1685
|
+
# @api private
|
|
1686
|
+
#
|
|
1687
|
+
# Opt into handling the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state.
|
|
1688
|
+
# A context enters this state when processing a bulkWrite command whose
|
|
1689
|
+
# target database differs from the command database ("admin").
|
|
1690
|
+
#
|
|
1691
|
+
# @param [ FFI::Pointer ] crypt A pointer to a mongocrypt_t object.
|
|
1692
|
+
attach_function(
|
|
1693
|
+
:mongocrypt_setopt_use_need_mongo_collinfo_with_db_state,
|
|
1694
|
+
[ :pointer ],
|
|
1695
|
+
:void
|
|
1696
|
+
)
|
|
1697
|
+
|
|
1698
|
+
# Opt into handling the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state.
|
|
1699
|
+
#
|
|
1700
|
+
# @param [ Mongo::Crypt::Handle ] handle
|
|
1701
|
+
def self.setopt_use_need_mongo_collinfo_with_db_state(handle)
|
|
1702
|
+
mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(handle.ref)
|
|
1703
|
+
end
|
|
1704
|
+
|
|
1705
|
+
# @!method self.mongocrypt_ctx_mongo_db(ctx)
|
|
1706
|
+
# @api private
|
|
1707
|
+
#
|
|
1708
|
+
# Get the database name for the current collinfo operation. Used in the
|
|
1709
|
+
# MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state to determine which
|
|
1710
|
+
# database to run listCollections against.
|
|
1711
|
+
#
|
|
1712
|
+
# @param [ FFI::Pointer ] ctx A pointer to a mongocrypt_ctx_t object.
|
|
1713
|
+
# @return [ String, nil ] The database name, or nil on error.
|
|
1714
|
+
attach_function :mongocrypt_ctx_mongo_db, [ :pointer ], :string
|
|
1715
|
+
|
|
1716
|
+
# Get the database name for the current collinfo operation.
|
|
1717
|
+
#
|
|
1718
|
+
# @param [ Mongo::Crypt::Context ] context
|
|
1719
|
+
# @return [ String ] The database name.
|
|
1720
|
+
def self.ctx_mongo_db(context)
|
|
1721
|
+
db_name = mongocrypt_ctx_mongo_db(context.ctx_p)
|
|
1722
|
+
return db_name if db_name
|
|
1723
|
+
|
|
1724
|
+
check_ctx_status(context)
|
|
1725
|
+
end
|
|
1726
|
+
|
|
1649
1727
|
# @!method self.mongocrypt_ctx_provide_kms_providers(ctx, kms_providers)
|
|
1650
1728
|
# @api private
|
|
1651
1729
|
#
|
data/lib/mongo/crypt/context.rb
CHANGED
|
@@ -76,7 +76,7 @@ module Mongo
|
|
|
76
76
|
# This method is not currently unit tested. It is integration tested
|
|
77
77
|
# in spec/integration/explicit_encryption_spec.rb
|
|
78
78
|
def run_state_machine(timeout_holder)
|
|
79
|
-
|
|
79
|
+
loop do
|
|
80
80
|
timeout_ms = timeout_holder.remaining_timeout_ms!
|
|
81
81
|
case state
|
|
82
82
|
when :error
|
|
@@ -90,6 +90,8 @@ module Mongo
|
|
|
90
90
|
provide_keys(timeout_ms)
|
|
91
91
|
when :need_mongo_collinfo
|
|
92
92
|
provide_collection_info(timeout_ms)
|
|
93
|
+
when :need_mongo_collinfo_with_db
|
|
94
|
+
provide_collection_info_with_db(timeout_ms)
|
|
93
95
|
when :need_mongo_markings
|
|
94
96
|
provide_markings(timeout_ms)
|
|
95
97
|
when :need_kms
|
|
@@ -122,17 +124,26 @@ module Mongo
|
|
|
122
124
|
def provide_markings(timeout_ms)
|
|
123
125
|
cmd = Binding.ctx_mongo_op(self)
|
|
124
126
|
|
|
125
|
-
result = @encryption_io.mark_command(cmd, timeout_ms: timeout_ms)
|
|
127
|
+
result = @encryption_io.mark_command(cmd, db_name: @db_name, timeout_ms: timeout_ms)
|
|
126
128
|
mongocrypt_feed(result)
|
|
127
129
|
|
|
128
130
|
mongocrypt_done
|
|
129
131
|
end
|
|
130
132
|
|
|
131
133
|
def provide_collection_info(timeout_ms)
|
|
134
|
+
feed_collection_info(@db_name, timeout_ms)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def provide_collection_info_with_db(timeout_ms)
|
|
138
|
+
feed_collection_info(Binding.ctx_mongo_db(self), timeout_ms)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def feed_collection_info(db_name, timeout_ms)
|
|
132
142
|
filter = Binding.ctx_mongo_op(self)
|
|
133
143
|
|
|
134
|
-
|
|
135
|
-
|
|
144
|
+
@encryption_io.collection_info(db_name, filter, timeout_ms: timeout_ms).each do |result|
|
|
145
|
+
mongocrypt_feed(result)
|
|
146
|
+
end
|
|
136
147
|
|
|
137
148
|
mongocrypt_done
|
|
138
149
|
end
|
|
@@ -159,12 +170,29 @@ module Mongo
|
|
|
159
170
|
raise unless e.network_error?
|
|
160
171
|
next if Binding.kms_ctx_fail(kms_context)
|
|
161
172
|
|
|
162
|
-
|
|
173
|
+
raise_kms_retry_error(kms_context, e)
|
|
163
174
|
end
|
|
164
175
|
end
|
|
165
176
|
Binding.ctx_kms_done(self)
|
|
166
177
|
end
|
|
167
178
|
|
|
179
|
+
# Raise a KmsError that wraps the KMS status message (which describes the
|
|
180
|
+
# retry exhaustion) with the error from the last attempt.
|
|
181
|
+
#
|
|
182
|
+
# @param [ Mongo::Crypt::KmsContext ] kms_context
|
|
183
|
+
# @param [ Mongo::Error::KmsError ] last_error The error from the last
|
|
184
|
+
# KMS request attempt.
|
|
185
|
+
#
|
|
186
|
+
# @raise [ Mongo::Error::KmsError ]
|
|
187
|
+
def raise_kms_retry_error(kms_context, last_error)
|
|
188
|
+
status = Binding.kms_ctx_status(kms_context)
|
|
189
|
+
raise Error::KmsError.new(
|
|
190
|
+
"#{status.message}, last attempt failed with: #{last_error.message}",
|
|
191
|
+
code: status.code,
|
|
192
|
+
network_error: true
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
|
|
168
196
|
# Indicate that state machine is done feeding I/O responses back to libmongocrypt
|
|
169
197
|
def mongocrypt_done
|
|
170
198
|
Binding.mongocrypt_ctx_mongo_done(ctx_p)
|
|
@@ -188,21 +216,24 @@ module Mongo
|
|
|
188
216
|
# KMS providers.
|
|
189
217
|
def retrieve_kms_credentials(timeout_holder)
|
|
190
218
|
providers = {}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
219
|
+
kms_providers.credentials_map.each do |identifier, creds|
|
|
220
|
+
next unless creds.empty?
|
|
221
|
+
|
|
222
|
+
case KMS.provider_base_type(identifier)
|
|
223
|
+
when 'aws'
|
|
224
|
+
begin
|
|
225
|
+
aws_credentials = Mongo::Auth::Aws::CredentialsRetriever.new.credentials(timeout_holder)
|
|
226
|
+
rescue Auth::Aws::CredentialsNotFound
|
|
227
|
+
raise Error::CryptError.new(
|
|
228
|
+
'Could not locate AWS credentials (checked environment variables, ECS and EC2 metadata)'
|
|
229
|
+
)
|
|
230
|
+
end
|
|
231
|
+
providers[identifier] = aws_credentials.to_h
|
|
232
|
+
when 'gcp'
|
|
233
|
+
providers[identifier] = { access_token: gcp_access_token(timeout_holder) }
|
|
234
|
+
when 'azure'
|
|
235
|
+
providers[identifier] = { access_token: azure_access_token(timeout_holder) }
|
|
198
236
|
end
|
|
199
|
-
providers[:aws] = aws_credentials.to_h
|
|
200
|
-
end
|
|
201
|
-
if kms_providers.gcp && kms_providers.gcp.empty?
|
|
202
|
-
providers[:gcp] = { access_token: gcp_access_token(timeout_holder) }
|
|
203
|
-
end
|
|
204
|
-
if kms_providers.azure && kms_providers.azure.empty?
|
|
205
|
-
providers[:azure] = { access_token: azure_access_token(timeout_holder) }
|
|
206
237
|
end
|
|
207
238
|
KMS::Credentials.new(providers)
|
|
208
239
|
end
|
|
@@ -97,7 +97,7 @@ module Mongo
|
|
|
97
97
|
# Must be a non-negative integer. An explicit value of 0 means infinite.
|
|
98
98
|
# The default value is unset which means the feature is not enabled.
|
|
99
99
|
#
|
|
100
|
-
# @return [
|
|
100
|
+
# @return [ Array<BSON::Document> ] The collection information documents
|
|
101
101
|
def collection_info(db_name, filter, timeout_ms: nil)
|
|
102
102
|
unless @metadata_client
|
|
103
103
|
raise ArgumentError,
|
|
@@ -108,18 +108,22 @@ module Mongo
|
|
|
108
108
|
.use(db_name)
|
|
109
109
|
.database
|
|
110
110
|
.list_collections(filter: filter, deserialize_as_bson: true, timeout_ms: timeout_ms)
|
|
111
|
-
.
|
|
111
|
+
.to_a
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
# Send the command to mongocryptd to be marked with intent-to-encrypt markings
|
|
115
115
|
#
|
|
116
116
|
# @param [ Hash ] cmd
|
|
117
|
+
# @param [ String | nil ] :db_name The database against which the command
|
|
118
|
+
# is being run. When provided, the command is sent to mongocryptd using
|
|
119
|
+
# this database so that the namespace in the command matches the namespace
|
|
120
|
+
# in encryptionInformation.
|
|
117
121
|
# @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
|
|
118
122
|
# Must be a non-negative integer. An explicit value of 0 means infinite.
|
|
119
123
|
# The default value is unset which means the feature is not enabled.
|
|
120
124
|
#
|
|
121
125
|
# @return [ Hash ] The marked command
|
|
122
|
-
def mark_command(cmd, timeout_ms: nil)
|
|
126
|
+
def mark_command(cmd, db_name: nil, timeout_ms: nil)
|
|
123
127
|
unless @mongocryptd_client
|
|
124
128
|
raise ArgumentError,
|
|
125
129
|
'mark_command requires mongocryptd_client to have been passed to the constructor, but it was not'
|
|
@@ -132,13 +136,15 @@ module Mongo
|
|
|
132
136
|
timeout_ms: timeout_ms
|
|
133
137
|
}
|
|
134
138
|
|
|
139
|
+
mongocryptd = db_name ? @mongocryptd_client.use(db_name) : @mongocryptd_client
|
|
140
|
+
|
|
135
141
|
begin
|
|
136
|
-
response =
|
|
142
|
+
response = mongocryptd.database.command(cmd, options)
|
|
137
143
|
rescue Error::NoServerAvailable => e
|
|
138
144
|
raise e if @options[:mongocryptd_bypass_spawn]
|
|
139
145
|
|
|
140
146
|
spawn_mongocryptd
|
|
141
|
-
response =
|
|
147
|
+
response = mongocryptd.database.command(cmd, options)
|
|
142
148
|
end
|
|
143
149
|
|
|
144
150
|
response.first
|
data/lib/mongo/crypt/handle.rb
CHANGED
|
@@ -74,6 +74,8 @@ module Mongo
|
|
|
74
74
|
Binding.method(:mongocrypt_destroy)
|
|
75
75
|
)
|
|
76
76
|
Binding.kms_ctx_setopt_retry_kms(self, true)
|
|
77
|
+
Binding.setopt_enable_multiple_collinfo(self)
|
|
78
|
+
Binding.setopt_use_need_mongo_collinfo_with_db_state(self)
|
|
77
79
|
@kms_providers = kms_providers
|
|
78
80
|
@kms_tls_options = kms_tls_options
|
|
79
81
|
|
|
@@ -101,7 +103,7 @@ module Mongo
|
|
|
101
103
|
|
|
102
104
|
Binding.setopt_kms_providers(self, @kms_providers.to_document)
|
|
103
105
|
|
|
104
|
-
if @kms_providers.
|
|
106
|
+
if @kms_providers.any_on_demand?
|
|
105
107
|
Binding.setopt_use_need_kms_credentials_state(self)
|
|
106
108
|
end
|
|
107
109
|
|
|
@@ -123,13 +125,23 @@ module Mongo
|
|
|
123
125
|
end
|
|
124
126
|
|
|
125
127
|
# Return TLS options for KMS provider. If there are no TLS options set,
|
|
126
|
-
# empty hash is returned.
|
|
128
|
+
# empty hash is returned. Named providers (e.g. "kmip:name1") fall back
|
|
129
|
+
# to the base-type options (e.g. :kmip) when no exact match is found.
|
|
127
130
|
#
|
|
128
|
-
# @param [ String ] provider
|
|
131
|
+
# @param [ String ] provider KMS provider name or named identifier.
|
|
129
132
|
#
|
|
130
133
|
# @return [ Hash ] TLS options to connect to KMS provider.
|
|
131
134
|
def kms_tls_options(provider)
|
|
132
|
-
|
|
135
|
+
provider_str = provider.to_s
|
|
136
|
+
base_type = KMS.provider_base_type(provider_str)
|
|
137
|
+
|
|
138
|
+
@kms_tls_options.fetch(provider_str) do
|
|
139
|
+
@kms_tls_options.fetch(provider_str.to_sym) do
|
|
140
|
+
@kms_tls_options.fetch(base_type) do
|
|
141
|
+
@kms_tls_options.fetch(base_type.to_sym, {})
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
133
145
|
end
|
|
134
146
|
|
|
135
147
|
def crypt_shared_lib_version
|
|
@@ -191,7 +203,7 @@ module Mongo
|
|
|
191
203
|
# Send the logs from libmongocrypt to the Mongo::Logger
|
|
192
204
|
def set_logger_callback
|
|
193
205
|
@log_callback = proc do |level, msg|
|
|
194
|
-
@logger.
|
|
206
|
+
@logger.public_send(level, msg)
|
|
195
207
|
end
|
|
196
208
|
|
|
197
209
|
Binding.setopt_log_handler(@mongocrypt, @log_callback)
|