mongo 2.18.0.beta1 → 2.18.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.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/mongo/bulk_write.rb +8 -2
  4. data/lib/mongo/client.rb +19 -5
  5. data/lib/mongo/client_encryption.rb +86 -4
  6. data/lib/mongo/cluster.rb +6 -4
  7. data/lib/mongo/collection/view/aggregation.rb +3 -0
  8. data/lib/mongo/collection/view/change_stream.rb +9 -0
  9. data/lib/mongo/collection/view/iterable.rb +1 -0
  10. data/lib/mongo/collection/view/readable.rb +11 -3
  11. data/lib/mongo/collection.rb +9 -1
  12. data/lib/mongo/config.rb +11 -0
  13. data/lib/mongo/crypt/auto_encrypter.rb +49 -21
  14. data/lib/mongo/crypt/binding.rb +73 -48
  15. data/lib/mongo/crypt/data_key_context.rb +6 -1
  16. data/lib/mongo/crypt/encryption_io.rb +66 -0
  17. data/lib/mongo/crypt/explicit_encrypter.rb +116 -5
  18. data/lib/mongo/crypt/explicit_encryption_context.rb +3 -8
  19. data/lib/mongo/crypt/handle.rb +26 -8
  20. data/lib/mongo/crypt/kms/aws.rb +11 -3
  21. data/lib/mongo/crypt/kms/azure.rb +14 -6
  22. data/lib/mongo/crypt/kms/gcp.rb +12 -5
  23. data/lib/mongo/crypt/kms/kmip.rb +15 -9
  24. data/lib/mongo/crypt/kms/local.rb +9 -1
  25. data/lib/mongo/crypt/kms/master_key_document.rb +1 -1
  26. data/lib/mongo/crypt/rewrap_many_data_key_context.rb +46 -0
  27. data/lib/mongo/crypt/rewrap_many_data_key_result.rb +37 -0
  28. data/lib/mongo/crypt/status.rb +8 -2
  29. data/lib/mongo/crypt.rb +2 -0
  30. data/lib/mongo/database.rb +10 -27
  31. data/lib/mongo/error/missing_file_chunk.rb +8 -2
  32. data/lib/mongo/grid/stream/read.rb +6 -0
  33. data/lib/mongo/index/view.rb +1 -0
  34. data/lib/mongo/operation/create/op_msg.rb +1 -13
  35. data/lib/mongo/operation/distinct/op_msg.rb +4 -1
  36. data/lib/mongo/protocol/msg.rb +0 -16
  37. data/lib/mongo/server/connection_pool.rb +5 -4
  38. data/lib/mongo/server/monitor/connection.rb +10 -4
  39. data/lib/mongo/server/monitor.rb +4 -0
  40. data/lib/mongo/server/push_monitor.rb +4 -0
  41. data/lib/mongo/version.rb +1 -1
  42. data/lib/mongo.rb +2 -0
  43. data/spec/README.md +14 -0
  44. data/spec/integration/change_stream_spec.rb +1 -1
  45. data/spec/integration/client_construction_spec.rb +73 -7
  46. data/spec/integration/client_side_encryption/auto_encryption_command_monitoring_spec.rb +165 -164
  47. data/spec/integration/client_side_encryption/decryption_events_prose_spec.rb +158 -0
  48. data/spec/integration/client_side_encryption/explicit_queryable_encryption_spec.rb +5 -5
  49. data/spec/integration/client_side_encryption/kms_tls_options_spec.rb +50 -8
  50. data/spec/integration/client_side_encryption/unique_index_on_key_alt_names_prose_spec.rb +85 -0
  51. data/spec/integration/ocsp_verifier_spec.rb +1 -1
  52. data/spec/integration/reconnect_spec.rb +2 -0
  53. data/spec/integration/sdam_events_spec.rb +40 -0
  54. data/spec/integration/srv_monitoring_spec.rb +1 -0
  55. data/spec/integration/srv_spec.rb +1 -0
  56. data/spec/lite_spec_helper.rb +5 -4
  57. data/spec/mongo/bulk_write_spec.rb +13 -0
  58. data/spec/mongo/client_construction_spec.rb +45 -2
  59. data/spec/mongo/client_encryption_spec.rb +0 -12
  60. data/spec/mongo/client_spec.rb +1 -1
  61. data/spec/mongo/collection/view/aggregation_spec.rb +119 -0
  62. data/spec/mongo/collection/view/readable_spec.rb +630 -5
  63. data/spec/mongo/collection_spec.rb +32 -0
  64. data/spec/mongo/crypt/auto_encrypter_spec.rb +110 -0
  65. data/spec/mongo/crypt/binding/context_spec.rb +3 -35
  66. data/spec/mongo/crypt/data_key_context_spec.rb +1 -1
  67. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +8 -3
  68. data/spec/mongo/crypt/handle_spec.rb +39 -3
  69. data/spec/mongo/crypt/kms/credentials_spec.rb +0 -47
  70. data/spec/mongo/index/view_spec.rb +56 -0
  71. data/spec/mongo/operation/create/op_msg_spec.rb +0 -42
  72. data/spec/mongo/server/connection_pool_spec.rb +26 -4
  73. data/spec/mongo/socket/ssl_spec.rb +3 -3
  74. data/spec/runners/crud/requirement.rb +6 -1
  75. data/spec/runners/crud/test.rb +1 -1
  76. data/spec/runners/transactions/spec.rb +2 -2
  77. data/spec/runners/transactions/test.rb +4 -20
  78. data/spec/runners/transactions.rb +2 -2
  79. data/spec/runners/unified/assertions.rb +32 -2
  80. data/spec/runners/unified/change_stream_operations.rb +3 -0
  81. data/spec/runners/unified/client_side_encryption_operations.rb +83 -0
  82. data/spec/runners/unified/crud_operations.rb +17 -2
  83. data/spec/runners/unified/ddl_operations.rb +27 -2
  84. data/spec/runners/unified/grid_fs_operations.rb +21 -0
  85. data/spec/runners/unified/test.rb +59 -1
  86. data/spec/shared/lib/mrss/docker_runner.rb +2 -0
  87. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  88. data/spec/shared/lib/mrss/lite_constraints.rb +10 -2
  89. data/spec/shared/shlib/set_env.sh +3 -0
  90. data/spec/solo/clean_exit_spec.rb +5 -0
  91. data/spec/spec_tests/client_side_encryption_spec.rb +1 -1
  92. data/spec/spec_tests/client_side_encryption_unified_spec.rb +16 -0
  93. data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +298 -0
  94. data/spec/spec_tests/data/client_side_encryption/create-and-createIndexes.yml +58 -0
  95. data/spec/spec_tests/data/client_side_encryption/fle2-Delete.yml +1 -1
  96. data/spec/spec_tests/data/client_side_encryption/fle2-EncryptedFields-vs-jsonSchema.yml +1 -1
  97. data/spec/spec_tests/data/client_side_encryption/fle2-FindOneAndUpdate.yml +2 -2
  98. data/spec/spec_tests/data/client_side_encryption/fle2-InsertFind-Indexed.yml +1 -1
  99. data/spec/spec_tests/data/client_side_encryption/fle2-Update.yml +2 -2
  100. data/spec/spec_tests/data/client_side_encryption/unified/addKeyAltName.yml +194 -0
  101. data/spec/spec_tests/data/client_side_encryption/unified/createDataKey-kms_providers-invalid.yml +67 -0
  102. data/spec/spec_tests/data/client_side_encryption/unified/createDataKey.yml +309 -0
  103. data/spec/spec_tests/data/client_side_encryption/unified/deleteKey.yml +159 -0
  104. data/spec/spec_tests/data/client_side_encryption/unified/getKey.yml +105 -0
  105. data/spec/spec_tests/data/client_side_encryption/unified/getKeyByAltName.yml +104 -0
  106. data/spec/spec_tests/data/client_side_encryption/unified/getKeys.yml +122 -0
  107. data/spec/spec_tests/data/client_side_encryption/unified/removeKeyAltName.yml +157 -0
  108. data/spec/spec_tests/data/client_side_encryption/unified/rewrapManyDataKey-decrypt_failure.yml +69 -0
  109. data/spec/spec_tests/data/client_side_encryption/unified/rewrapManyDataKey-encrypt_failure.yml +122 -0
  110. data/spec/spec_tests/data/client_side_encryption/unified/rewrapManyDataKey.yml +432 -0
  111. data/spec/spec_tests/data/client_side_encryption/validatorAndPartialFieldExpression.yml +166 -0
  112. data/spec/spec_tests/data/command_monitoring_unified/bulkWrite.yml +68 -0
  113. data/spec/spec_tests/data/command_monitoring_unified/command.yml +50 -0
  114. data/spec/spec_tests/data/command_monitoring_unified/deleteMany.yml +79 -0
  115. data/spec/spec_tests/data/command_monitoring_unified/deleteOne.yml +79 -0
  116. data/spec/spec_tests/data/command_monitoring_unified/find.yml +254 -0
  117. data/spec/spec_tests/data/command_monitoring_unified/insertMany.yml +79 -0
  118. data/spec/spec_tests/data/command_monitoring_unified/insertOne.yml +77 -0
  119. data/spec/spec_tests/data/command_monitoring_unified/unacknowledgedBulkWrite.yml +55 -0
  120. data/spec/spec_tests/data/command_monitoring_unified/updateMany.yml +87 -0
  121. data/spec/spec_tests/data/command_monitoring_unified/updateOne.yml +118 -0
  122. data/spec/spec_tests/data/crud_unified/distinct-comment.yml +98 -0
  123. data/spec/spec_tests/data/gridfs_unified/delete.yml +198 -0
  124. data/spec/spec_tests/data/gridfs_unified/download.yml +241 -0
  125. data/spec/spec_tests/data/gridfs_unified/downloadByName.yml +159 -0
  126. data/spec/spec_tests/data/gridfs_unified/upload-disableMD5.yml +92 -0
  127. data/spec/spec_tests/data/gridfs_unified/upload.yml +288 -0
  128. data/spec/spec_tests/gridfs_unified_spec.rb +13 -0
  129. data/spec/stress/connection_pool_timing_spec.rb +2 -2
  130. data/spec/support/background_thread_registry.rb +3 -13
  131. data/spec/support/certificates/atlas-ocsp-ca.crt +40 -47
  132. data/spec/support/certificates/atlas-ocsp.crt +101 -106
  133. data/spec/support/crypt.rb +57 -13
  134. data/spec/support/macros.rb +10 -0
  135. data/spec/support/spec_config.rb +4 -0
  136. data.tar.gz.sig +0 -0
  137. metadata +1271 -1219
  138. metadata.gz.sig +0 -0
  139. data/spec/spec_tests/command_monitoring_spec.rb +0 -71
  140. data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +0 -49
  141. data/spec/spec_tests/data/command_monitoring/command.yml +0 -61
  142. data/spec/spec_tests/data/command_monitoring/deleteMany.yml +0 -55
  143. data/spec/spec_tests/data/command_monitoring/deleteOne.yml +0 -55
  144. data/spec/spec_tests/data/command_monitoring/find.yml +0 -266
  145. data/spec/spec_tests/data/command_monitoring/insertMany.yml +0 -75
  146. data/spec/spec_tests/data/command_monitoring/insertOne.yml +0 -51
  147. data/spec/spec_tests/data/command_monitoring/unacknowledgedBulkWrite.yml +0 -34
  148. data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -65
  149. data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -90
@@ -475,6 +475,38 @@ module Mongo
475
475
  end
476
476
  end
477
477
 
478
+ # @!method self.mongocrypt_ctx_setopt_key_material(ctx, binary)
479
+ # @api private
480
+ #
481
+ # When creating a data key, set a custom key material to use for
482
+ # encrypting data.
483
+ # @param [ FFI::Pointer ] ctx A pointer to a mongocrypt_ctx_t object.
484
+ # @param [ FFI::Pointer ] binary A pointer to a mongocrypt_binary_t
485
+ # object that references the data encryption key to use.
486
+ # @return [ Boolean ] Whether the custom key material was successfully set.
487
+ # @note Do not initialize ctx before calling this method.
488
+ attach_function(
489
+ :mongocrypt_ctx_setopt_key_material,
490
+ [:pointer, :pointer],
491
+ :bool
492
+ )
493
+
494
+ # Set set a custom key material to use for
495
+ # encrypting data.
496
+ #
497
+ # @param [ Mongo::Crypt::Context ] context A DataKeyContext
498
+ # @param [ BSON::Binary ] key_material 96 bytes of custom key material
499
+ #
500
+ # @raise [ Mongo::Error::CryptError ] If the key material is not 96 bytes.
501
+ def self.ctx_setopt_key_material(context, key_material)
502
+ data = {'keyMaterial' => key_material}.to_bson.to_s
503
+ Binary.wrap_string(data) do |data_p|
504
+ check_ctx_status(context) do
505
+ mongocrypt_ctx_setopt_key_material(context.ctx_p, data_p)
506
+ end
507
+ end
508
+ end
509
+
478
510
  # @!method self.mongocrypt_ctx_setopt_algorithm(ctx, algorithm, len)
479
511
  # @api private
480
512
  #
@@ -562,6 +594,40 @@ module Mongo
562
594
  end
563
595
  end
564
596
 
597
+ # @!method self.mongocrypt_ctx_datakey_init(ctx, filter)
598
+ # @api private
599
+ #
600
+ # Initialize a context to rewrap datakeys.
601
+ #
602
+ # @param [ FFI::Pointer ] ctx A pointer to a mongocrypt_ctx_t object.
603
+ # @param [ FFI::Pointer ] filter A pointer to a mongocrypt_binary_t object
604
+ # that represents filter to use for the find command on the key vault
605
+ # collection to retrieve datakeys to rewrap.
606
+ #
607
+ # @return [ Boolean ] Whether the initialization was successful.
608
+ attach_function(
609
+ :mongocrypt_ctx_rewrap_many_datakey_init,
610
+ [:pointer, :pointer],
611
+ :bool
612
+ )
613
+
614
+ # Initialize a context to rewrap datakeys.
615
+ #
616
+ # @param [ Mongo::Crypt::Context ] context
617
+ # @param [ BSON::Document ] filter BSON Document
618
+ # that represents filter to use for the find command on the key vault
619
+ # collection to retrieve datakeys to rewrap.
620
+ #
621
+ # @return [ Boolean ] Whether the initialization was successful.
622
+ def self.ctx_rewrap_many_datakey_init(context, filter)
623
+ filter_data = filter.to_bson.to_s
624
+ Binary.wrap_string(filter_data) do |data_p|
625
+ check_ctx_status(context) do
626
+ mongocrypt_ctx_rewrap_many_datakey_init(context.ctx_p, data_p)
627
+ end
628
+ end
629
+ end
630
+
565
631
  # @!method self.mongocrypt_ctx_encrypt_init(ctx, db, db_len, cmd)
566
632
  # @api private
567
633
  #
@@ -974,7 +1040,7 @@ module Mongo
974
1040
  status = Status.new
975
1041
 
976
1042
  mongocrypt_kms_ctx_status(kms_context.kms_ctx_p, status.ref)
977
- status.raise_crypt_error
1043
+ status.raise_crypt_error(kms: true)
978
1044
  end
979
1045
  end
980
1046
 
@@ -1319,48 +1385,6 @@ module Mongo
1319
1385
  end
1320
1386
  end
1321
1387
 
1322
- enum :mongocrypt_index_type, [
1323
- :none, 1,
1324
- :equality
1325
- ]
1326
-
1327
- # @!method self.mongocrypt_ctx_setopt_index_type(ctx, mongocrypt_index_type)
1328
- # @api private
1329
- #
1330
- # Set the index type used for explicit encryption.
1331
- # The index type is only used for FLE 2 encryption.
1332
- #
1333
- # @param [ FFI::Pointer ] ctx A pointer to a mongocrypt_ctx_t object.
1334
- # @param[ mongocrypt_index_type ] index_type Type of the index.
1335
- #
1336
- # @return [ Boolean ] Whether setting this option succeeded.
1337
- attach_function(
1338
- :mongocrypt_ctx_setopt_index_type,
1339
- [
1340
- :pointer,
1341
- :mongocrypt_index_type
1342
- ],
1343
- :bool
1344
- )
1345
-
1346
- # Set the index type used for explicit encryption.
1347
- # The index type is only used for FLE 2 encryption.
1348
- #
1349
- # @param [ Mongo::Crypt::Context ] context Explicit encryption context.
1350
- # @param [ Symbol ] :mongocrypt_index_type index_type Type of the index.
1351
- # Allowed values are :none, :equality.
1352
- #
1353
- # @raise [ Mongo::Error::CryptError ] If the operation failed.
1354
- def self.ctx_setopt_index_type(context, index_type)
1355
- check_ctx_status(context) do
1356
- mongocrypt_ctx_setopt_index_type(context.ctx_p, index_type)
1357
- end
1358
- end
1359
-
1360
- enum :mongocrypt_query_type, [
1361
- :equality, 1
1362
- ]
1363
-
1364
1388
  # @!method self.mongocrypt_ctx_setopt_query_type(ctx, mongocrypt_query_type)
1365
1389
  # @api private
1366
1390
  #
@@ -1368,14 +1392,16 @@ module Mongo
1368
1392
  # The query type is only used for indexed FLE 2 encryption.
1369
1393
  #
1370
1394
  # @param [ FFI::Pointer ] ctx A pointer to a mongocrypt_ctx_t object.
1371
- # @param [ mongocrypt_query_type ] query_type Type of the query.
1395
+ # @param [ String ] query_type Type of the query.
1396
+ # @param [ Integer ] len The length of the query type string.
1372
1397
  #
1373
1398
  # @return [ Boolean ] Whether setting this option succeeded.
1374
1399
  attach_function(
1375
1400
  :mongocrypt_ctx_setopt_query_type,
1376
1401
  [
1377
1402
  :pointer,
1378
- :mongocrypt_query_type
1403
+ :string,
1404
+ :int
1379
1405
  ],
1380
1406
  :bool
1381
1407
  )
@@ -1384,13 +1410,12 @@ module Mongo
1384
1410
  # The query type is only used for indexed FLE 2 encryption.
1385
1411
  #
1386
1412
  # @param [ Mongo::Crypt::Context ] context Explicit encryption context.
1387
- # @param [ Symbol ] :mongocrypt_query_type query_type Type of the query.
1388
- # Allowed value is :equality.
1413
+ # @param [ String ] :mongocrypt_query_type query_type Type of the query.
1389
1414
  #
1390
1415
  # @raise [ Mongo::Error::CryptError ] If the operation failed.
1391
1416
  def self.ctx_setopt_query_type(context, query_type)
1392
1417
  check_ctx_status(context) do
1393
- mongocrypt_ctx_setopt_query_type(context.ctx_p, query_type)
1418
+ mongocrypt_ctx_setopt_query_type(context.ctx_p, query_type, -1)
1394
1419
  end
1395
1420
  end
1396
1421
 
@@ -34,10 +34,15 @@ module Mongo
34
34
  # key document that contains master encryption key parameters.
35
35
  # @param [ Array<String> | nil ] key_alt_names An optional array of strings specifying
36
36
  # alternate names for the new data key.
37
- def initialize(mongocrypt, io, master_key_document, key_alt_names = nil)
37
+ # @param [ String | nil ] :key_material Optional
38
+ # 96 bytes to use as custom key material for the data key being created.
39
+ # If :key_material option is given, the custom key material is used
40
+ # for encrypting and decrypting data.
41
+ def initialize(mongocrypt, io, master_key_document, key_alt_names, key_material)
38
42
  super(mongocrypt, io)
39
43
  Binding.ctx_setopt_key_encryption_key(self, master_key_document.to_document)
40
44
  set_key_alt_names(key_alt_names) if key_alt_names
45
+ Binding.ctx_setopt_key_material(self, BSON::Binary.new(key_material)) if key_material
41
46
  initialize_ctx
42
47
  end
43
48
 
@@ -158,6 +158,72 @@ module Mongo
158
158
  end
159
159
  end
160
160
 
161
+ # Adds a key_alt_name to the key_alt_names array of the key document
162
+ # in the key vault collection with the given id.
163
+ def add_key_alt_name(id, key_alt_name)
164
+ key_vault_collection.find_one_and_update(
165
+ { _id: id },
166
+ { '$addToSet' => { keyAltNames: key_alt_name } },
167
+ )
168
+ end
169
+
170
+ # Removes the key document with the given id
171
+ # from the key vault collection.
172
+ def delete_key(id)
173
+ key_vault_collection.delete_one(_id: id)
174
+ end
175
+
176
+ # Finds a single key document with the given id.
177
+ def get_key(id)
178
+ key_vault_collection.find(_id: id).first
179
+ end
180
+
181
+ # Returns a key document in the key vault collection with
182
+ # the given key_alt_name.
183
+ def get_key_by_alt_name(key_alt_name)
184
+ key_vault_collection.find(keyAltNames: key_alt_name).first
185
+ end
186
+
187
+ # Finds all documents in the key vault collection.
188
+ def get_keys
189
+ key_vault_collection.find
190
+ end
191
+
192
+ # Removes a key_alt_name from the key_alt_names array of the key document
193
+ # in the key vault collection with the given id.
194
+ def remove_key_alt_name(id, key_alt_name)
195
+ key_vault_collection.find_one_and_update(
196
+ { _id: id },
197
+ [
198
+ {
199
+ '$set' => {
200
+ keyAltNames: {
201
+ '$cond' => [
202
+ { '$eq' => [ '$keyAltNames', [ key_alt_name ] ] },
203
+ '$$REMOVE',
204
+ {
205
+ '$filter' => {
206
+ input: '$keyAltNames',
207
+ cond: { '$ne' => [ '$$this', key_alt_name ] }
208
+ }
209
+ }
210
+ ]
211
+ }
212
+ }
213
+ }
214
+ ]
215
+ )
216
+ end
217
+
218
+ # Apply given requests to the key vault collection using bulk write.
219
+ #
220
+ # @param [ Array<Hash> ] requests The bulk write requests.
221
+ #
222
+ # @return [ BulkWrite::Result ] The result of the operation.
223
+ def update_data_keys(updates)
224
+ key_vault_collection.bulk_write(updates)
225
+ end
226
+
161
227
  private
162
228
 
163
229
  def validate_key_vault_client!(key_vault_client)
@@ -40,7 +40,7 @@ module Mongo
40
40
  @encryption_io = EncryptionIO.new(
41
41
  key_vault_client: key_vault_client,
42
42
  metadata_client: nil,
43
- key_vault_namespace: key_vault_namespace
43
+ key_vault_namespace: key_vault_namespace,
44
44
  )
45
45
  end
46
46
 
@@ -52,15 +52,20 @@ module Mongo
52
52
  # key document that contains master encryption key parameters.
53
53
  # @param [ Array<String> | nil ] key_alt_names An optional array of strings specifying
54
54
  # alternate names for the new data key.
55
+ # @param [ String | nil ] key_material Optional 96 bytes to use as
56
+ # custom key material for the data key being created.
57
+ # If key_material option is given, the custom key material is used
58
+ # for encrypting and decrypting data.
55
59
  #
56
60
  # @return [ BSON::Binary ] The 16-byte UUID of the new data key as a
57
61
  # BSON::Binary object with type :uuid.
58
- def create_and_insert_data_key(master_key_document, key_alt_names)
62
+ def create_and_insert_data_key(master_key_document, key_alt_names, key_material = nil)
59
63
  data_key_document = Crypt::DataKeyContext.new(
60
64
  @crypt_handle,
61
65
  @encryption_io,
62
66
  master_key_document,
63
- key_alt_names
67
+ key_alt_names,
68
+ key_material
64
69
  ).run_state_machine
65
70
 
66
71
  @encryption_io.insert_data_key(data_key_document).inserted_id
@@ -83,10 +88,10 @@ module Mongo
83
88
  # to be applied if encryption algorithm is set to "Indexed". If not
84
89
  # provided, it defaults to a value of 0. Contention factor should be set
85
90
  # only if encryption algorithm is set to "Indexed".
86
- # @option options [ Symbol ] query_type Query type to be applied
91
+ # @option options [ String | nil ] query_type Query type to be applied
87
92
  # if encryption algorithm is set to "Indexed". Query type should be set
88
93
  # only if encryption algorithm is set to "Indexed". The only allowed
89
- # value is :equality.
94
+ # value is "equality".
90
95
  #
91
96
  # @note The :key_id and :key_alt_name options are mutually exclusive. Only
92
97
  # one is required to perform explicit encryption.
@@ -117,6 +122,112 @@ module Mongo
117
122
  { 'v': value },
118
123
  ).run_state_machine['v']
119
124
  end
125
+
126
+ # Adds a key_alt_name for the key in the key vault collection with the given id.
127
+ #
128
+ # @param [ BSON::Binary ] id Id of the key to add new key alt name.
129
+ # @param [ String ] key_alt_name New key alt name to add.
130
+ #
131
+ # @return [ BSON::Document | nil ] Document describing the identified key
132
+ # before adding the key alt name, or nil if no such key.
133
+ def add_key_alt_name(id, key_alt_name)
134
+ @encryption_io.add_key_alt_name(id, key_alt_name)
135
+ end
136
+
137
+ # Removes the key with the given id from the key vault collection.
138
+ #
139
+ # @param [ BSON::Binary ] id Id of the key to delete.
140
+ #
141
+ # @return [ Operation::Result ] The response from the database for the delete_one
142
+ # operation that deletes the key.
143
+ def delete_key(id)
144
+ @encryption_io.delete_key(id)
145
+ end
146
+
147
+ # Finds a single key with the given id.
148
+ #
149
+ # @param [ BSON::Binary ] id Id of the key to get.
150
+ #
151
+ # @return [ BSON::Document | nil ] The found key document or nil
152
+ # if not found.
153
+ def get_key(id)
154
+ @encryption_io.get_key(id)
155
+ end
156
+
157
+ # Returns a key in the key vault collection with the given key_alt_name.
158
+ #
159
+ # @param [ String ] key_alt_name Key alt name to find a key.
160
+ #
161
+ # @return [ BSON::Document | nil ] The found key document or nil
162
+ # if not found.
163
+ def get_key_by_alt_name(key_alt_name)
164
+ @encryption_io.get_key_by_alt_name(key_alt_name)
165
+ end
166
+
167
+ # Returns all keys in the key vault collection.
168
+ #
169
+ # @return [ Collection::View ] Keys in the key vault collection.
170
+ def get_keys
171
+ @encryption_io.get_keys
172
+ end
173
+
174
+ # Removes a key_alt_name from a key in the key vault collection with the given id.
175
+ #
176
+ # @param [ BSON::Binary ] id Id of the key to remove key alt name.
177
+ # @param [ String ] key_alt_name Key alt name to remove.
178
+ #
179
+ # @return [ BSON::Document | nil ] Document describing the identified key
180
+ # before removing the key alt name, or nil if no such key.
181
+ def remove_key_alt_name(id, key_alt_name)
182
+ @encryption_io.remove_key_alt_name(id, key_alt_name)
183
+ end
184
+
185
+ # Decrypts multiple data keys and (re-)encrypts them with a new master_key,
186
+ # or with their current master_key if a new one is not given.
187
+ #
188
+ # @param [ Hash ] filter Filter used to find keys to be updated.
189
+ # @param [ Hash ] options
190
+ #
191
+ # @option options [ String ] :provider KMS provider to encrypt keys.
192
+ # @option options [ Hash | nil ] :master_key Document describing master key
193
+ # to encrypt keys.
194
+ #
195
+ # @return [ Crypt::RewrapManyDataKeyResult ] Result of the operation.
196
+ def rewrap_many_data_key(filter, opts = {})
197
+ master_key_document = if opts[:provider]
198
+ options = opts.dup
199
+ provider = options.delete(:provider)
200
+ KMS::MasterKeyDocument.new(provider, options)
201
+ end
202
+
203
+ rewrap_result = Crypt::RewrapManyDataKeyContext.new(
204
+ @crypt_handle,
205
+ @encryption_io,
206
+ filter,
207
+ master_key_document
208
+ ).run_state_machine
209
+ if rewrap_result.nil?
210
+ return RewrapManyDataKeyResult.new(nil)
211
+ end
212
+ data_key_documents = rewrap_result.fetch('v')
213
+ updates = data_key_documents.map do |doc|
214
+ {
215
+ update_one: {
216
+ filter: { _id: doc[:_id] },
217
+ update: {
218
+ '$set' => {
219
+ masterKey: doc[:masterKey],
220
+ keyMaterial: doc[:keyMaterial]
221
+ },
222
+ '$currentDate' => { updateDate: true },
223
+ },
224
+ }
225
+ }
226
+ end
227
+ RewrapManyDataKeyResult.new(
228
+ @encryption_io.update_data_keys(updates)
229
+ )
230
+ end
120
231
  end
121
232
  end
122
233
  end
@@ -44,10 +44,10 @@ module Mongo
44
44
  # to be applied if encryption algorithm is set to "Indexed". If not
45
45
  # provided, it defaults to a value of 0. Contention factor should be set
46
46
  # only if encryption algorithm is set to "Indexed".
47
- # @option options [ Symbol ] query_type Query type to be applied
47
+ # @option options [ String | nil ] query_type Query type to be applied
48
48
  # if encryption algorithm is set to "Indexed". Query type should be set
49
49
  # only if encryption algorithm is set to "Indexed". The only allowed
50
- # value is :equality.
50
+ # value is "equality".
51
51
  #
52
52
  # @raise [ ArgumentError|Mongo::Error::CryptError ] If invalid options are provided
53
53
  def initialize(mongocrypt, io, doc, options={})
@@ -89,6 +89,7 @@ module Mongo
89
89
 
90
90
  # Set the algorithm option on the mongocrypt_ctx_t object and raises
91
91
  # an exception if the algorithm is invalid.
92
+ Binding.ctx_setopt_algorithm(self, options[:algorithm])
92
93
  if options[:algorithm] == 'Indexed'
93
94
  if options[:contention_factor]
94
95
  Binding.ctx_setopt_contention_factor(self, options[:contention_factor])
@@ -96,7 +97,6 @@ module Mongo
96
97
  if options[:query_type]
97
98
  Binding.ctx_setopt_query_type(self, options[:query_type])
98
99
  end
99
- Binding.ctx_setopt_index_type(self, :equality)
100
100
  else
101
101
  if options[:contention_factor]
102
102
  raise ArgumentError.new(':contention_factor is allowed only for "Indexed" algorithm')
@@ -104,11 +104,6 @@ module Mongo
104
104
  if options[:query_type]
105
105
  raise ArgumentError.new(':query_type is allowed only for "Indexed" algorithm')
106
106
  end
107
- if options[:algorithm] == 'Unindexed'
108
- Binding.ctx_setopt_index_type(self, :none)
109
- else
110
- Binding.ctx_setopt_algorithm(self, options[:algorithm])
111
- end
112
107
  end
113
108
 
114
109
  # Initializes the mongocrypt_ctx_t object for explicit encryption and
@@ -39,7 +39,11 @@ module Mongo
39
39
  #
40
40
  # @param [ Hash ] options A hash of options.
41
41
  # @option options [ Hash | nil ] :schema_map A hash representing the JSON schema
42
- # of the collection that stores auto encrypted documents.
42
+ # of the collection that stores auto encrypted documents. This option is
43
+ # mutually exclusive with :schema_map_path.
44
+ # @option options [ String | nil ] :schema_map_path A path to a file contains the JSON schema
45
+ # of the collection that stores auto encrypted documents. This option is
46
+ # mutually exclusive with :schema_map.
43
47
  # @option options [ Hash | nil ] :encrypted_fields_map maps a collection
44
48
  # namespace to an encryptedFields.
45
49
  # - Note: If a collection is present on both the encryptedFieldsMap
@@ -58,8 +62,7 @@ module Mongo
58
62
 
59
63
  @kms_tls_options = kms_tls_options
60
64
 
61
- @schema_map = options[:schema_map]
62
- set_schema_map if @schema_map
65
+ maybe_set_schema_map(options)
63
66
 
64
67
  @encrypted_fields_map = options[:encrypted_fields_map]
65
68
  set_encrypted_fields_map if @encrypted_fields_map
@@ -96,14 +99,29 @@ module Mongo
96
99
  private
97
100
 
98
101
  # Set the schema map option on the underlying mongocrypt_t object
99
- def set_schema_map
100
- unless @schema_map.is_a?(Hash)
102
+ def maybe_set_schema_map(options)
103
+ if !options[:schema_map] && !options[:schema_map_path]
104
+ @schema_map = nil
105
+ elsif options[:schema_map] && options[:schema_map_path]
101
106
  raise ArgumentError.new(
102
- "#{@schema_map} is an invalid schema_map; schema_map must be a Hash or nil"
107
+ "Cannot set both schema_map and schema_map_path options."
103
108
  )
109
+ elsif options[:schema_map]
110
+ unless options[:schema_map].is_a?(Hash)
111
+ raise ArgumentError.new(
112
+ "#{@schema_map} is an invalid schema_map; schema_map must be a Hash or nil."
113
+ )
114
+ end
115
+ @schema_map = options[:schema_map]
116
+ Binding.setopt_schema_map(self, @schema_map)
117
+ elsif options[:schema_map_path]
118
+ @schema_map = BSON::ExtJSON.parse(File.read(options[:schema_map_path]))
119
+ Binding.setopt_schema_map(self, @schema_map)
104
120
  end
105
-
106
- Binding.setopt_schema_map(self, @schema_map)
121
+ rescue Errno::ENOENT
122
+ raise ArgumentError.new(
123
+ "#{@schema_map_path} is an invalid path to a file contains schema_map."
124
+ )
107
125
  end
108
126
 
109
127
  def set_encrypted_fields_map
@@ -24,6 +24,7 @@ module Mongo
24
24
  #
25
25
  # @api private
26
26
  class Credentials
27
+ extend Forwardable
27
28
  include KMS::Validations
28
29
 
29
30
  # @return [ String ] AWS access key.
@@ -35,6 +36,9 @@ module Mongo
35
36
  # @return [ String | nil ] AWS session token.
36
37
  attr_reader :session_token
37
38
 
39
+ # @api private
40
+ def_delegator :@opts, :empty?
41
+
38
42
  FORMAT_HINT = "AWS KMS provider options must be in the format: " +
39
43
  "{ access_key_id: 'YOUR-ACCESS-KEY-ID', secret_access_key: 'SECRET-ACCESS-KEY' }"
40
44
 
@@ -49,15 +53,19 @@ module Mongo
49
53
  # @raise [ ArgumentError ] If required options are missing or incorrectly
50
54
  # formatted.
51
55
  def initialize(opts)
52
- @access_key_id = validate_param(:access_key_id, opts, FORMAT_HINT)
53
- @secret_access_key = validate_param(:secret_access_key, opts, FORMAT_HINT)
54
- @session_token = validate_param(:session_token, opts, FORMAT_HINT, required: false)
56
+ @opts = opts
57
+ unless empty?
58
+ @access_key_id = validate_param(:access_key_id, opts, FORMAT_HINT)
59
+ @secret_access_key = validate_param(:secret_access_key, opts, FORMAT_HINT)
60
+ @session_token = validate_param(:session_token, opts, FORMAT_HINT, required: false)
61
+ end
55
62
  end
56
63
 
57
64
  # Convert credentials object to a BSON document in libmongocrypt format.
58
65
  #
59
66
  # @return [ BSON::Document ] AWS KMS credentials in libmongocrypt format.
60
67
  def to_document
68
+ return BSON::Document.new if empty?
61
69
  BSON::Document.new({
62
70
  accessKeyId: access_key_id,
63
71
  secretAccessKey: secret_access_key,
@@ -23,6 +23,7 @@ module Mongo
23
23
  #
24
24
  # @api private
25
25
  class Credentials
26
+ extend Forwardable
26
27
  include KMS::Validations
27
28
 
28
29
  # @return [ String ] Azure tenant id.
@@ -37,6 +38,9 @@ module Mongo
37
38
  # @return [ String | nil ] Azure identity platform endpoint.
38
39
  attr_reader :identity_platform_endpoint
39
40
 
41
+ # @api private
42
+ def_delegator :@opts, :empty?
43
+
40
44
  FORMAT_HINT = "Azure KMS provider options must be in the format: " +
41
45
  "{ tenant_id: 'TENANT-ID', client_id: 'TENANT_ID', client_secret: 'CLIENT_SECRET' }"
42
46
 
@@ -53,18 +57,22 @@ module Mongo
53
57
  # @raise [ ArgumentError ] If required options are missing or incorrectly
54
58
  # formatted.
55
59
  def initialize(opts)
56
- @tenant_id = validate_param(:tenant_id, opts, FORMAT_HINT)
57
- @client_id = validate_param(:client_id, opts, FORMAT_HINT)
58
- @client_secret = validate_param(:client_secret, opts, FORMAT_HINT)
59
- @identity_platform_endpoint = validate_param(
60
- :identity_platform_endpoint, opts, FORMAT_HINT, required: false
61
- )
60
+ @opts = opts
61
+ unless empty?
62
+ @tenant_id = validate_param(:tenant_id, opts, FORMAT_HINT)
63
+ @client_id = validate_param(:client_id, opts, FORMAT_HINT)
64
+ @client_secret = validate_param(:client_secret, opts, FORMAT_HINT)
65
+ @identity_platform_endpoint = validate_param(
66
+ :identity_platform_endpoint, opts, FORMAT_HINT, required: false
67
+ )
68
+ end
62
69
  end
63
70
 
64
71
  # Convert credentials object to a BSON document in libmongocrypt format.
65
72
  #
66
73
  # @return [ BSON::Document ] Azure KMS credentials in libmongocrypt format.
67
74
  def to_document
75
+ return BSON::Document.new if empty?
68
76
  BSON::Document.new({
69
77
  tenantId: @tenant_id,
70
78
  clientId: @client_id,
@@ -24,6 +24,7 @@ module Mongo
24
24
  #
25
25
  # @api private
26
26
  class Credentials
27
+ extend Forwardable
27
28
  include KMS::Validations
28
29
 
29
30
  # @return [ String ] GCP email to authenticate with.
@@ -35,6 +36,9 @@ module Mongo
35
36
  # @return [ String | nil ] GCP KMS endpoint.
36
37
  attr_reader :endpoint
37
38
 
39
+ # @api private
40
+ def_delegator :@opts, :empty?
41
+
38
42
  FORMAT_HINT = "GCP KMS provider options must be in the format: " +
39
43
  "{ email: 'EMAIL', private_key: 'PRIVATE-KEY' }"
40
44
 
@@ -50,8 +54,10 @@ module Mongo
50
54
  # @raise [ ArgumentError ] If required options are missing or incorrectly
51
55
  # formatted.
52
56
  def initialize(opts)
53
- @email = validate_param(:email, opts, FORMAT_HINT)
57
+ @opts = opts
58
+ return if empty?
54
59
 
60
+ @email = validate_param(:email, opts, FORMAT_HINT)
55
61
  @private_key = begin
56
62
  private_key_opt = validate_param(:private_key, opts, FORMAT_HINT)
57
63
  if BSON::Environment.jruby?
@@ -91,6 +97,7 @@ module Mongo
91
97
  #
92
98
  # @return [ BSON::Document ] Azure KMS credentials in libmongocrypt format.
93
99
  def to_document
100
+ return BSON::Document.new if empty?
94
101
  BSON::Document.new({
95
102
  email: email,
96
103
  privateKey: BSON::Binary.new(private_key, :generic),
@@ -143,10 +150,9 @@ module Mongo
143
150
  #
144
151
  # @raise [ ArgumentError ] If required options are missing or incorrectly.
145
152
  def initialize(opts)
146
- unless opts.is_a?(Hash)
147
- raise ArgumentError.new(
148
- 'Key document options must contain a key named :master_key with a Hash value'
149
- )
153
+ if opts.empty?
154
+ @empty = true
155
+ return
150
156
  end
151
157
  @project_id = validate_param(:project_id, opts, FORMAT_HINT)
152
158
  @location = validate_param(:location, opts, FORMAT_HINT)
@@ -160,6 +166,7 @@ module Mongo
160
166
  #
161
167
  # @return [ BSON::Document ] GCP KMS credentials in libmongocrypt format.
162
168
  def to_document
169
+ return BSON::Document.new({}) if @empty
163
170
  BSON::Document.new({
164
171
  provider: 'gcp',
165
172
  projectId: project_id,