Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gemfiles/standard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ def standard_dependencies
gem 'ruby-lsp', platforms: :mri
end

gem 'libmongocrypt-helper', '~> 1.14.0' if %w[helper mongocryptd].include?(ENV['FLE'])
gem 'libmongocrypt-helper', '~> 1.19.0' if %w[helper mongocryptd].include?(ENV['FLE'])
end
# rubocop:enable Metrics/MethodLength
2 changes: 1 addition & 1 deletion lib/mongo/crypt/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def bson_mode
def provide_markings(timeout_ms)
cmd = Binding.ctx_mongo_op(self)

result = @encryption_io.mark_command(cmd, timeout_ms: timeout_ms)
result = @encryption_io.mark_command(cmd, db_name: @db_name, timeout_ms: timeout_ms)
mongocrypt_feed(result)

mongocrypt_done
Expand Down
12 changes: 9 additions & 3 deletions lib/mongo/crypt/encryption_io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,16 @@ def collection_info(db_name, filter, timeout_ms: nil)
# Send the command to mongocryptd to be marked with intent-to-encrypt markings
#
# @param [ Hash ] cmd
# @param [ String | nil ] :db_name The database against which the command
# is being run. When provided, the command is sent to mongocryptd using
# this database so that the namespace in the command matches the namespace
# in encryptionInformation.
# @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
# Must be a non-negative integer. An explicit value of 0 means infinite.
# The default value is unset which means the feature is not enabled.
#
# @return [ Hash ] The marked command
def mark_command(cmd, timeout_ms: nil)
def mark_command(cmd, db_name: nil, timeout_ms: nil)
unless @mongocryptd_client
raise ArgumentError,
'mark_command requires mongocryptd_client to have been passed to the constructor, but it was not'
Expand All @@ -132,13 +136,15 @@ def mark_command(cmd, timeout_ms: nil)
timeout_ms: timeout_ms
}

mongocryptd = db_name ? @mongocryptd_client.use(db_name) : @mongocryptd_client

begin
response = @mongocryptd_client.database.command(cmd, options)
response = mongocryptd.database.command(cmd, options)
rescue Error::NoServerAvailable => e
raise e if @options[:mongocryptd_bypass_spawn]

spawn_mongocryptd
response = @mongocryptd_client.database.command(cmd, options)
response = mongocryptd.database.command(cmd, options)
end

response.first
Expand Down
10 changes: 8 additions & 2 deletions spec/runners/crud/requirement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def initialize(spec)
end
@server_parameters = spec['serverParameters']
@auth = spec['auth']
@csfle = !!spec['csfle'] if spec['csfle']
@csfle = spec['csfle'] if spec['csfle']
end

attr_reader :min_server_version, :max_server_version, :topologies
Expand Down Expand Up @@ -113,7 +113,13 @@ def satisfied?
elsif @auth == false
ok &&= !SpecConfig.instance.auth?
end
ok &&= !!(ENV['LIBMONGOCRYPT_PATH'] || ENV['FLE']) if @csfle
if @csfle
ok &&= !!(ENV['LIBMONGOCRYPT_PATH'] || ENV['FLE'])
if ok && @csfle.is_a?(Hash) && (min_version = @csfle['minLibmongocryptVersion'])
actual_version = Mongo::Crypt::Binding.mongocrypt_version(nil)
ok &&= Mongo::Crypt::Binding.parse_version(actual_version) >= Gem::Version.new(min_version)
end
end
ok
end

Expand Down
4 changes: 4 additions & 0 deletions spec/runners/unified/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ def generate_entities(es)

await_min_pool_size_ms = spec.use('awaitMinPoolSizeMS')

if auto_encrypt_opts = spec.use('autoEncryptOpts')
opts.merge!(Utils.convert_client_options('autoEncryptOpts' => auto_encrypt_opts))
end

create_client(**opts).tap do |client|
@observe_sensitive[id] = spec.use('observeSensitiveCommands')
@subscribers[client] ||= subscriber
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
description: fle2v2-InsertFind-keyAltName
schemaVersion: "1.25"
runOnRequirements:
- minServerVersion: "7.0.0"
# Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol.
# FLE 2 Encrypted collections are not supported on standalone.
topologies: ["replicaset", "sharded", "load-balanced"]
csfle:
minLibmongocryptVersion: "1.18.0"
createEntities:
- client:
id: "client0"
autoEncryptOpts:
keyVaultNamespace: keyvault.datakeys
kmsProviders:
local:
key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk
encryptedFieldsMap:
default.default:
{
"fields":
[
{
"path": "encryptedIndexed",
Comment thread
jamis marked this conversation as resolved.
"bsonType": "string",
"queries": { "queryType": "equality", "contention": { "$numberLong": "0" } },
"keyAltName": "altname"
},
],
}
observeEvents:
- commandStartedEvent
- database:
id: "db"
client: "client0"
databaseName: default
- collection:
id: "coll"
database: "db"
collectionName: &collection_name default
- client:
id: "client_unencrypted"
observeEvents:
- commandStartedEvent
- database:
id: "db_unencrypted"
client: "client_unencrypted"
databaseName: default
- collection:
id: "coll_unencrypted"
database: "db_unencrypted"
collectionName: default
initialData:
- databaseName: default
collectionName: default
documents: []
createOptions:
encryptedFields:
&encrypted_fields {
"fields":
[
{
"keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } },
"path": "encryptedIndexed",
"bsonType": "string",
"queries": { "queryType": "equality", "contention": { "$numberLong": "0" } },
}
],
}
- databaseName: keyvault
collectionName: datakeys
documents:
[
{
"_id": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } },
"keyMaterial":
{
"$binary":
{
"base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==",
"subType": "00",
},
},
"creationDate": { "$date": { "$numberLong": "1648914851981" } },
"updateDate": { "$date": { "$numberLong": "1648914851981" } },
"status": { "$numberInt": "0" },
"masterKey": { "provider": "local" },
"keyAltNames": ["altname"],
},
]
tests:
- description: "Insert and find FLE2 indexed field"
operations:
- name: insertOne
arguments:
document: &doc0 { _id: 1, encryptedIndexed: "123" }
object: "coll"
- name: find
arguments:
filter: { encryptedIndexed: "123" }
object: "coll"
expectResult: [*doc0]
- name: find
object: coll_unencrypted
arguments:
filter: {}
expectResult:
- {
"_id": 1,
"encryptedIndexed": { $$type: "binData" },
"__safeContent__":
["$binary": { "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType": "00" }],
}
expectEvents:
- client: "client0"
events:
- commandStartedEvent:
command:
find: datakeys
filter:
{
"$or":
[
"_id": { "$in": [] },
"keyAltNames": { "$in": ["altname"] },
],
}
$db: keyvault
readConcern: { level: "majority" }
commandName: find
- commandStartedEvent:
command:
insert: *collection_name
documents:
- { "_id": 1, "encryptedIndexed": { $$type: "binData" } }
ordered: true
encryptionInformation:
type: 1
schema:
default.default:
# libmongocrypt applies escCollection and ecocCollection to outgoing command.
escCollection: "enxcol_.default.esc"
ecocCollection: "enxcol_.default.ecoc"
<<: *encrypted_fields
commandName: insert
- commandStartedEvent:
command:
find: *collection_name
filter:
{
"encryptedIndexed":
{
"$eq":
{
"$binary":
{
"base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA",
"subType": "06",
},
},
},
}
encryptionInformation:
type: 1
schema:
default.default:
# libmongocrypt applies escCollection and ecocCollection to outgoing command.
escCollection: "enxcol_.default.esc"
ecocCollection: "enxcol_.default.ecoc"
<<: *encrypted_fields
commandName: find
- description: "Create translates keyAltName"
operations:
- name: dropCollection # Drop collection if already exists
object: "db"
arguments:
collection: *collection_name
- name: createCollection # Uses encrypted client with encryptedFieldsMap. keyAltName translated to keyId.
object: "db"
arguments:
collection: *collection_name
expectEvents:
- client: "client0"
events:

# events from dropCollection ... begin
- commandStartedEvent:
command:
drop: &esc_collection_name enxcol_.default.esc
commandName: drop
- commandStartedEvent:
command:
drop: &ecoc_collection_name enxcol_.default.ecoc
commandName: drop
- commandStartedEvent:
command:
drop: *collection_name
commandName: drop
# events from dropCollection ... end

# events from createCollection ... begin
- commandStartedEvent:
command:
create: *esc_collection_name
clusteredIndex: {key: {_id: 1}, unique: true}
commandName: create
- commandStartedEvent:
command:
create: *ecoc_collection_name
clusteredIndex: {key: {_id: 1}, unique: true}
commandName: create
- commandStartedEvent:
command:
find: datakeys # To translate keyAltName to keyId.
filter:
{
"$or":
[
"_id": { "$in": [] },
"keyAltNames": { "$in": ["altname"] },
],
}
$db: keyvault
readConcern: { level: "majority" }
commandName: find
- commandStartedEvent:
command:
create: *collection_name
encryptedFields: &encryptedFields_translated {
"fields":
[
{
"path": "encryptedIndexed",
"bsonType": "string",
"queries": { "queryType": "equality", "contention": { "$numberLong": "0" } },
"keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }
}
]
}
commandName: create
# Index on __safeContents__ is then created.
- commandStartedEvent:
command:
createIndexes: *collection_name
indexes:
- name: __safeContent___1
key: { __safeContent__: 1 }
commandName: createIndexes
# events from createCollection ... end
11 changes: 8 additions & 3 deletions spec/support/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,14 @@ def _apply_kms_provider_gcp(opts, auto_encrypt_opts)
def _apply_kms_provider_local(opts, auto_encrypt_opts)
return unless opts['kmsProviders']['local']

auto_encrypt_opts[:kms_providers][:local] = {
key: BSON::ExtJSON.parse_obj(opts['kmsProviders']['local']['key']).data
}
key_value = opts['kmsProviders']['local']['key']
key_bytes = if key_value.is_a?(Hash)
BSON::ExtJSON.parse_obj(key_value).data
else
Base64.strict_decode64(key_value)
end

auto_encrypt_opts[:kms_providers][:local] = { key: key_bytes }
end

def _apply_key_vault_namespace(opts, auto_encrypt_opts)
Expand Down
Loading