Skip to content

Commit 8f31376

Browse files
RUBY-2980 Add missed QE encryption tests (#2517)
1 parent 8c6c388 commit 8f31376

File tree

19 files changed

+440
-51
lines changed

19 files changed

+440
-51
lines changed

.evergreen/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1349,7 +1349,7 @@ buildvariants:
13491349
matrix_spec:
13501350
auth-and-ssl: "noauth-and-nossl"
13511351
ruby: [ruby-3.1, ruby-3.0, ruby-2.7]
1352-
topology: standalone
1352+
topology: [replica-set, sharded-cluster]
13531353
mongodb-version: ['6.0']
13541354
os: ubuntu2004
13551355
fle: helper

.evergreen/config/standard.yml.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ buildvariants:
471471
matrix_spec:
472472
auth-and-ssl: "noauth-and-nossl"
473473
ruby: [ruby-3.1, ruby-3.0, ruby-2.7]
474-
topology: standalone
474+
topology: [replica-set, sharded-cluster]
475475
mongodb-version: ['6.0']
476476
os: ubuntu2004
477477
fle: helper

gemfiles/standard.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,6 @@ def standard_dependencies
6060
end
6161

6262
if ENV['FLE'] == 'helper'
63-
gem 'libmongocrypt-helper', '~> 1.5.0.alpha1.0.1001'
63+
gem 'libmongocrypt-helper', '~> 1.5.0.rc1.0.1001'
6464
end
6565
end

lib/mongo/collection.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ class Collection
7070
:expire_after => :expireAfterSeconds,
7171
:clustered_index => :clusteredIndex,
7272
:change_stream_pre_and_post_images => :changeStreamPreAndPostImages,
73-
:encrypted_fields => :encryptedFields
73+
:encrypted_fields => :encryptedFields,
74+
:validator => :validator,
7475
}
7576

7677
# Check if a collection is equal to another object. Will check the name and
@@ -258,6 +259,8 @@ def capped?
258259
# pre- and post-images on the created collection.
259260
# @option opts [ Hash ] :encrypted_fields Hash describing encrypted fields
260261
# for queryable encryption.
262+
# @option opts [ Hash ] :validator Hash describing document validation
263+
# options for the collection.
261264
#
262265
# @return [ Result ] The result of the command.
263266
#
@@ -298,6 +301,7 @@ def create(opts = {})
298301
# taken from options passed to the create method.
299302
collation: options[:collation] || options['collation'],
300303
encrypted_fields: encrypted_fields,
304+
validator: options[:validator],
301305
).execute(next_primary(nil, session), context: context)
302306
end
303307
end

lib/mongo/crypt/auto_encrypter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ def initialize(options)
9898
@options[:extra_options][:mongocryptd_uri],
9999
monitoring_io: @options[:client].options[:monitoring_io],
100100
server_selection_timeout: 10,
101+
database: @options[:client].options[:database]
101102
)
102103

103104
begin

lib/mongo/database.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,33 @@ def command(operation, opts = {})
225225

226226
client.send(:with_session, opts) do |session|
227227
server = selector.select_server(cluster, nil, session)
228+
# This code MUST be removed as soon as server starts accepting
229+
# contention as int32.
230+
_operation = operation.dup
231+
if _operation['encryptedFields'] && _operation['encryptedFields'].key?('fields')
232+
_operation['encryptedFields']['fields'] = _operation['encryptedFields']['fields'].map do |field|
233+
if field['queries'] && field['queries'].key?('contention')
234+
field['queries']['contention'] = BSON::Int64.new(field['queries']['contention'])
235+
end
236+
field
237+
end
238+
end
239+
if schema = _operation.dig('encryptionInformation', 'schema')
240+
_operation['encryptionInformation']['schema'] = schema.map do |coll, params|
241+
if params['fields']
242+
params['fields'] = params['fields'].map do |field|
243+
if contention = field.dig('queries', 'contention')
244+
field['queries']['contention'] = BSON::Int64.new(contention)
245+
end
246+
field
247+
end
248+
end
249+
[coll, params]
250+
end.to_h
251+
end
252+
# End of code to be removed
228253
op = Operation::Command.new(
229-
:selector => operation.dup,
254+
:selector => _operation,
230255
:db_name => name,
231256
:read => selector,
232257
:session => session

lib/mongo/operation/create/op_msg.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,17 @@ def selector(connection)
3535
collation: spec[:collation],
3636
encryptedFields: spec[:encrypted_fields],
3737
).compact.tap do |sel|
38-
if sel[:encryptedFields] && sel[:encryptedFields].key?(:fields)
39-
sel[:encryptedFields][:fields] = sel[:encryptedFields][:fields].map do |field|
40-
if field[:queries] && field[:queries].key?(:contention)
41-
field[:queries][:contention] = BSON::Int64.new(field[:queries][:contention])
38+
# This code MUST be removed as soon as server starts accepting
39+
# contention as int32.
40+
if sel[:encryptedFields] && sel[:encryptedFields].key?('fields')
41+
sel[:encryptedFields]['fields'] = sel[:encryptedFields]['fields'].map do |field|
42+
if field['queries'] && field['queries'].key?('contention')
43+
field['queries']['contention'] = BSON::Int64.new(field['queries']['contention'])
4244
end
4345
field
4446
end
4547
end
48+
# End of code to be removed
4649
end
4750
end
4851
end

lib/mongo/protocol/msg.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ def maybe_encrypt(connection, context)
229229
if cmd.key?('$db') && !enc_cmd.key?('$db')
230230
enc_cmd['$db'] = cmd['$db']
231231
end
232+
# This code MUST be removed as soon as server starts accepting
233+
# contention as int32.
232234
if schema = enc_cmd.dig('encryptionInformation', 'schema')
233235
enc_cmd['encryptionInformation']['schema'] = schema.map do |coll, params|
234236
if params['fields']
@@ -242,6 +244,7 @@ def maybe_encrypt(connection, context)
242244
[coll, params]
243245
end.to_h
244246
end
247+
# End of code to be removed
245248

246249
Msg.new(@flags, @options, enc_cmd)
247250
else

spec/integration/client_side_encryption/bson_size_limit_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
}
5353
].create
5454

55-
key_vault_collection = client.use('admin')['datakeys', write_concern: { w: :majority }]
55+
key_vault_collection = client.use('keyvault')['datakeys', write_concern: { w: :majority }]
5656

5757
key_vault_collection.drop
5858
key_vault_collection.insert_one(
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# frozen_string_literal: true
2+
# encoding: utf-8
3+
4+
require 'spec_helper'
5+
6+
describe 'Explicit Queryable Encryption' do
7+
require_libmongocrypt
8+
min_server_version '6.0'
9+
require_topology :replica_set, :sharded, :load_balanced
10+
11+
include_context 'define shared FLE helpers'
12+
13+
let(:key1_id) do
14+
key1_document['_id']
15+
end
16+
17+
let(:encrypted_coll) do
18+
'explicit_encryption'
19+
end
20+
21+
let(:value) do
22+
"encrypted indexed value"
23+
end
24+
25+
let(:unindexed_value) do
26+
"encrypted unindexed value"
27+
end
28+
29+
let(:key_vault_client) do
30+
ClientRegistry.instance.new_local_client(SpecConfig.instance.addresses)
31+
end
32+
33+
let(:client_encryption_opts) do
34+
{
35+
kms_providers: local_kms_providers,
36+
kms_tls_options: kms_tls_options,
37+
key_vault_namespace: key_vault_namespace
38+
}
39+
end
40+
41+
let(:client_encryption) do
42+
Mongo::ClientEncryption.new(
43+
key_vault_client,
44+
client_encryption_opts
45+
)
46+
end
47+
48+
let(:encrypted_client) do
49+
ClientRegistry.instance.new_local_client(
50+
SpecConfig.instance.addresses,
51+
auto_encryption_options: {
52+
key_vault_namespace: "#{key_vault_db}.#{key_vault_coll}",
53+
kms_providers: local_kms_providers,
54+
bypass_query_analysis: true
55+
},
56+
database: SpecConfig.instance.test_db
57+
)
58+
end
59+
60+
before(:each) do
61+
authorized_client[encrypted_coll].drop(encrypted_fields: encrypted_fields)
62+
authorized_client[encrypted_coll].create(encrypted_fields: encrypted_fields)
63+
authorized_client.use(key_vault_db)[key_vault_coll].drop
64+
authorized_client.use(key_vault_db)[key_vault_coll, write_concern: {w: :majority}].insert_one(key1_document)
65+
end
66+
67+
after(:each) do
68+
authorized_client[encrypted_coll].drop(encrypted_fields: encrypted_fields)
69+
authorized_client.use(key_vault_db)[key_vault_coll].drop
70+
end
71+
72+
it 'can insert encrypted indexed and find' do
73+
insert_payload = client_encryption.encrypt(
74+
value, key_id: key1_id, algorithm: "Indexed"
75+
)
76+
encrypted_client[encrypted_coll].insert_one(
77+
"encryptedIndexed" => insert_payload
78+
)
79+
find_payload = client_encryption.encrypt(
80+
value, key_id: key1_id, algorithm: "Indexed", query_type: :equality
81+
)
82+
find_results = encrypted_client[encrypted_coll]
83+
.find("encryptedIndexed" => find_payload)
84+
.to_a
85+
expect(find_results.size).to eq(1)
86+
expect(find_results.first["encryptedIndexed"]).to eq(value)
87+
end
88+
89+
it 'can insert encrypted indexed and find with non-zero contention' do
90+
10.times do
91+
insert_payload = client_encryption.encrypt(
92+
value, key_id: key1_id, algorithm: "Indexed", contention_factor: 10
93+
)
94+
encrypted_client[encrypted_coll].insert_one(
95+
"encryptedIndexed" => insert_payload
96+
)
97+
end
98+
find_payload = client_encryption.encrypt(
99+
value, key_id: key1_id, algorithm: "Indexed", query_type: :equality
100+
)
101+
find_results = encrypted_client[encrypted_coll]
102+
.find("encryptedIndexed" => find_payload)
103+
.to_a
104+
expect(find_results.size).to be < 10
105+
find_results.each do |doc|
106+
expect(doc["encryptedIndexed"]).to eq(value)
107+
end
108+
find_payload_2 = client_encryption.encrypt(
109+
value, key_id: key1_id, algorithm: "Indexed", query_type: :equality, contention_factor: 10
110+
)
111+
find_results_2 = encrypted_client[encrypted_coll]
112+
.find("encryptedIndexed" => find_payload_2)
113+
.to_a
114+
expect(find_results_2.size).to eq(10)
115+
find_results_2.each do |doc|
116+
expect(doc["encryptedIndexed"]).to eq(value)
117+
end
118+
end
119+
120+
it 'can insert encrypted unindexed' do
121+
insert_payload = client_encryption.encrypt(
122+
unindexed_value, key_id: key1_id, algorithm: "Unindexed"
123+
)
124+
encrypted_client[encrypted_coll].insert_one(
125+
"_id" => 1, "encryptedUnindexed" => insert_payload
126+
)
127+
find_results = encrypted_client[encrypted_coll].find("_id" => 1).to_a
128+
expect(find_results.size).to eq(1)
129+
expect(find_results.first["encryptedUnindexed"]).to eq(unindexed_value)
130+
end
131+
132+
it 'can roundtrip encrypted indexed' do
133+
payload = client_encryption.encrypt(
134+
value, key_id: key1_id, algorithm: "Indexed"
135+
)
136+
decrypted_value = client_encryption.decrypt(payload)
137+
expect(decrypted_value).to eq(value)
138+
end
139+
140+
it 'can roundtrip encrypted unindexed' do
141+
payload = client_encryption.encrypt(
142+
unindexed_value, key_id: key1_id, algorithm: "Unindexed"
143+
)
144+
decrypted_value = client_encryption.decrypt(payload)
145+
expect(decrypted_value).to eq(unindexed_value)
146+
end
147+
end

0 commit comments

Comments
 (0)