Class: Mongo::Crypt::EncryptionIO Private
Relationships & Source Files | |
Inherits: | Object |
Defined in: | lib/mongo/crypt/encryption_io.rb |
Overview
A class that implements I/O methods between the driver and the MongoDB server or mongocryptd.
Constant Summary
-
SOCKET_TIMEOUT =
::Mongo::Timeout
used for TLS socket connection, reading, and writing. There is no specific timeout written in the spec. See SPEC-1394 for a discussion and updates on what this timeout should be.10
Class Method Summary
-
.new(client: nil, mongocryptd_client: nil, key_vault_namespace:, key_vault_client:, metadata_client:, mongocryptd_options: {}) ⇒ EncryptionIO
constructor
Internal use only
Creates a new
EncryptionIO
object with information about how to connect to the key vault.
Instance Method Summary
-
#add_key_alt_name(id, key_alt_name, timeout_ms: nil)
Internal use only
Adds a key_alt_name to the key_alt_names array of the key document in the key vault collection with the given id.
-
#collection_info(db_name, filter, timeout_ms: nil) ⇒ Hash
Internal use only
Get collection info for a collection matching the provided filter.
-
#delete_key(id, timeout_ms: nil)
Internal use only
Removes the key document with the given id from the key vault collection.
-
#feed_kms(kms_context, tls_options, timeout_ms: nil)
Internal use only
Get information about the remote
KMS
encryption key and feed it to the theKmsContext
object. -
#find_keys(filter, timeout_ms: nil) ⇒ Array<BSON::Document>
Internal use only
Query for keys in the key vault collection using the provided filter.
-
#get_key(id, timeout_ms: nil)
Internal use only
Finds a single key document with the given id.
-
#get_key_by_alt_name(key_alt_name, timeout_ms: nil)
Internal use only
Returns a key document in the key vault collection with the given key_alt_name.
-
#get_keys(timeout_ms: nil)
Internal use only
Finds all documents in the key vault collection.
-
#insert_data_key(document, timeout_ms: nil) ⇒ Mongo::Operation::Insert::Result
Internal use only
Insert a document into the key vault collection.
-
#mark_command(cmd, timeout_ms: nil) ⇒ Hash
Internal use only
Send the command to mongocryptd to be marked with intent-to-encrypt markings.
-
#remove_key_alt_name(id, key_alt_name, timeout_ms: nil)
Internal use only
Removes a key_alt_name from the key_alt_names array of the key document in the key vault collection with the given id.
-
#update_data_keys(updates, timeout_ms: nil) ⇒ BulkWrite::Result
Internal use only
Apply given requests to the key vault collection using bulk write.
-
#key_vault_collection
private
Internal use only
Use the provided key vault client and namespace to construct a
::Mongo::Collection
object representing the key vault collection. -
#spawn_mongocryptd ⇒ Integer
private
Internal use only
Spawn a new mongocryptd process using the mongocryptd_spawn_path and mongocryptd_spawn_args passed in through the extra auto encrypt options.
- #validate_key_vault_client!(key_vault_client) private Internal use only
- #validate_key_vault_namespace!(key_vault_namespace) private Internal use only
-
#with_ssl_socket(endpoint, tls_options, timeout_ms: nil) {|ssl_socket| ... }
private
Internal use only
Provide a TLS socket to be used for
KMS
calls in a block API.
Instance Method Details
#add_key_alt_name(id, key_alt_name, timeout_ms: nil)
Adds a key_alt_name to the key_alt_names array of the key document in the key vault collection with the given id.
# File 'lib/mongo/crypt/encryption_io.rb', line 185
def add_key_alt_name(id, key_alt_name, timeout_ms: nil) key_vault_collection.find_one_and_update( { _id: id }, { '$addToSet' => { keyAltNames: key_alt_name } }, timeout_ms: timeout_ms ) end
#collection_info(db_name, filter, timeout_ms: nil) ⇒ Hash
Get collection info for a collection matching the provided filter
# File 'lib/mongo/crypt/encryption_io.rb', line 105
def collection_info(db_name, filter, timeout_ms: nil) unless @metadata_client raise ArgumentError, 'collection_info requires metadata_client to have been passed to the constructor, but it was not' end @metadata_client .use(db_name) .database .list_collections(filter: filter, deserialize_as_bson: true, timeout_ms: timeout_ms) .first end
#delete_key(id, timeout_ms: nil)
Removes the key document with the given id from the key vault collection.
# File 'lib/mongo/crypt/encryption_io.rb', line 195
def delete_key(id, timeout_ms: nil) key_vault_collection.delete_one(_id: id, timeout_ms: timeout_ms) end
#feed_kms(kms_context, tls_options, timeout_ms: nil)
Get information about the remote KMS
encryption key and feed it to the the KmsContext
object
# File 'lib/mongo/crypt/encryption_io.rb', line 161
def feed_kms(kms_context, , timeout_ms: nil) with_ssl_socket(kms_context.endpoint, ) do |ssl_socket| Timeout.timeout(timeout_ms || SOCKET_TIMEOUT, Error::SocketTimeoutError, 'Socket write operation timed out' ) do ssl_socket.syswrite(kms_context. ) end bytes_needed = kms_context.bytes_needed while bytes_needed > 0 do bytes = Timeout.timeout(timeout_ms || SOCKET_TIMEOUT, Error::SocketTimeoutError, 'Socket read operation timed out' ) do ssl_socket.sysread(bytes_needed) end kms_context.feed(bytes) bytes_needed = kms_context.bytes_needed end end end
#find_keys(filter, timeout_ms: nil) ⇒ Array
<BSON::Document
>
Query for keys in the key vault collection using the provided filter
# File 'lib/mongo/crypt/encryption_io.rb', line 81
def find_keys(filter, timeout_ms: nil) key_vault_collection.find(filter, timeout_ms: timeout_ms).to_a end
#get_key(id, timeout_ms: nil)
Finds a single key document with the given id.
# File 'lib/mongo/crypt/encryption_io.rb', line 200
def get_key(id, timeout_ms: nil) key_vault_collection.find(_id: id, timeout_ms: timeout_ms).first end
#get_key_by_alt_name(key_alt_name, timeout_ms: nil)
Returns a key document in the key vault collection with the given key_alt_name.
# File 'lib/mongo/crypt/encryption_io.rb', line 206
def get_key_by_alt_name(key_alt_name, timeout_ms: nil) key_vault_collection.find(keyAltNames: key_alt_name, timeout_ms: timeout_ms).first end
#get_keys(timeout_ms: nil)
Finds all documents in the key vault collection.
# File 'lib/mongo/crypt/encryption_io.rb', line 211
def get_keys(timeout_ms: nil) key_vault_collection.find(nil, timeout_ms: timeout_ms) end
#insert_data_key(document, timeout_ms: nil) ⇒ Mongo::Operation::Insert::Result
Insert a document into the key vault collection
# File 'lib/mongo/crypt/encryption_io.rb', line 93
def insert_data_key(document, timeout_ms: nil) key_vault_collection.insert_one(document, timeout_ms: timeout_ms) end
#key_vault_collection (private)
Use the provided key vault client and namespace to construct a ::Mongo::Collection
object representing the key vault collection.
# File 'lib/mongo/crypt/encryption_io.rb', line 280
def key_vault_collection @key_vault_collection ||= @key_vault_client.with( database: @key_vault_db_name, read_concern: { level: :majority }, write_concern: { w: :majority } )[@key_vault_collection_name] end
#mark_command(cmd, timeout_ms: nil) ⇒ Hash
Send the command to mongocryptd to be marked with intent-to-encrypt markings
# File 'lib/mongo/crypt/encryption_io.rb', line 125
def mark_command(cmd, timeout_ms: nil) unless @mongocryptd_client raise ArgumentError, 'mark_command requires mongocryptd_client to have been passed to the constructor, but it was not' end # Ensure the response from mongocryptd is deserialized with { mode: :bson } # to prevent losing type information in commands = { execution_options: { deserialize_as_bson: true }, timeout_ms: timeout_ms } begin response = @mongocryptd_client.database.command(cmd, ) rescue Error::NoServerAvailable => e raise e if @options[:mongocryptd_bypass_spawn] spawn_mongocryptd response = @mongocryptd_client.database.command(cmd, ) end return response.first end
#remove_key_alt_name(id, key_alt_name, timeout_ms: nil)
Removes a key_alt_name from the key_alt_names array of the key document in the key vault collection with the given id.
# File 'lib/mongo/crypt/encryption_io.rb', line 217
def remove_key_alt_name(id, key_alt_name, timeout_ms: nil) key_vault_collection.find_one_and_update( { _id: id }, [ { '$set' => { keyAltNames: { '$cond' => [ { '$eq' => [ '$keyAltNames', [ key_alt_name ] ] }, '$$REMOVE', { '$filter' => { input: '$keyAltNames', cond: { '$ne' => [ '$$this', key_alt_name ] } } } ] } } } ], timeout_ms: timeout_ms ) end
#spawn_mongocryptd ⇒ Integer
(private)
To capture the mongocryptd logs, add “–logpath=/path/to/logs” to auto_encryption_options -> extra_options -> mongocrpytd_spawn_args
Spawn a new mongocryptd process using the mongocryptd_spawn_path and mongocryptd_spawn_args passed in through the extra auto encrypt options. Stdout and Stderr of this new process are written to /dev/null.
# File 'lib/mongo/crypt/encryption_io.rb', line 300
def spawn_mongocryptd mongocryptd_spawn_args = @options[:mongocryptd_spawn_args] mongocryptd_spawn_path = @options[:mongocryptd_spawn_path] unless mongocryptd_spawn_path raise ArgumentError.new( 'Cannot spawn mongocryptd process when no ' + ':mongocryptd_spawn_path option is provided' ) end if mongocryptd_spawn_path.nil? || mongocryptd_spawn_args.nil? || mongocryptd_spawn_args.empty? then raise ArgumentError.new( 'Cannot spawn mongocryptd process when no :mongocryptd_spawn_args ' + 'option is provided. To start mongocryptd without arguments, pass ' + '"--" for :mongocryptd_spawn_args' ) end begin Process.spawn( mongocryptd_spawn_path, *mongocryptd_spawn_args, [:out, :err]=>'/dev/null' ) rescue Errno::ENOENT => e raise Error::MongocryptdSpawnError.new( "Failed to spawn mongocryptd at the path \"#{mongocryptd_spawn_path}\" " + "with arguments #{mongocryptd_spawn_args}. Received error " + "#{e.class}: \"#{e.}\"" ) end end
#update_data_keys(updates, timeout_ms: nil) ⇒ BulkWrite::Result
Apply given requests to the key vault collection using bulk write.
# File 'lib/mongo/crypt/encryption_io.rb', line 247
def update_data_keys(updates, timeout_ms: nil) key_vault_collection.bulk_write(updates, timeout_ms: timeout_ms) end
#validate_key_vault_client!(key_vault_client) (private)
[ GitHub ]# File 'lib/mongo/crypt/encryption_io.rb', line 253
def validate_key_vault_client!(key_vault_client) unless key_vault_client raise ArgumentError.new('The :key_vault_client option cannot be nil') end unless key_vault_client.is_a?(Client) raise ArgumentError.new( 'The :key_vault_client option must be an instance of Mongo::Client' ) end end
#validate_key_vault_namespace!(key_vault_namespace) (private)
[ GitHub ]# File 'lib/mongo/crypt/encryption_io.rb', line 265
def validate_key_vault_namespace!(key_vault_namespace) unless key_vault_namespace raise ArgumentError.new('The :key_vault_namespace option cannot be nil') end unless key_vault_namespace.split('.').length == 2 raise ArgumentError.new( "#{key_vault_namespace} is an invalid key vault namespace." + "The :key_vault_namespace option must be in the format database.collection" ) end end
#with_ssl_socket(endpoint, tls_options, timeout_ms: nil) {|ssl_socket| ... } (private)
The socket is always closed when the provided block has finished executing
Provide a TLS socket to be used for KMS
calls in a block API
# File 'lib/mongo/crypt/encryption_io.rb', line 349
def with_ssl_socket(endpoint, , timeout_ms: nil) csot = !timeout_ms.nil? address = begin host, port = endpoint.split(':') port ||= 443 # All supported KMS APIs use this port by default. Address.new([host, port].join(':')) end = { ssl: true, csot: csot }.tap do |opts| if csot opts[:connect_timeout] = (timeout_ms / 1_000.0) end end mongo_socket = address.socket( SOCKET_TIMEOUT, .merge( ) ) yield(mongo_socket.socket) rescue Error::KmsError raise rescue StandardError => e raise Error::KmsError.new("Error when connecting to KMS provider: #{e.class}: #{e.}", network_error: true) ensure mongo_socket&.close end