mongo 2.18.0.beta1 → 2.18.0

Sign up to get free protection for your applications and to get access to all the features.
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,