34
34
from dynamodb_encryption_sdk .encrypted .table import EncryptedTable
35
35
from dynamodb_encryption_sdk .identifiers import CryptoAction
36
36
from dynamodb_encryption_sdk .internal .identifiers import ReservedAttributes
37
+ from dynamodb_encryption_sdk .material_providers import CryptographicMaterialsProvider
37
38
from dynamodb_encryption_sdk .material_providers .most_recent import MostRecentProvider
38
39
from dynamodb_encryption_sdk .material_providers .static import StaticCryptographicMaterialsProvider
39
40
from dynamodb_encryption_sdk .material_providers .store .meta import MetaStore
40
41
from dynamodb_encryption_sdk .material_providers .wrapped import WrappedCryptographicMaterialsProvider
42
+ from dynamodb_encryption_sdk .materials import CryptographicMaterials
41
43
from dynamodb_encryption_sdk .materials .raw import RawDecryptionMaterials , RawEncryptionMaterials
42
- from dynamodb_encryption_sdk .structures import AttributeActions
44
+ from dynamodb_encryption_sdk .structures import AttributeActions , EncryptionContext
43
45
from dynamodb_encryption_sdk .transform import ddb_to_dict , dict_to_ddb
44
46
45
47
RUNNING_IN_TRAVIS = "TRAVIS" in os .environ
@@ -164,6 +166,38 @@ def table_with_global_secondary_indexes():
164
166
mock_dynamodb2 ().stop ()
165
167
166
168
169
+ class PassThroughCryptographicMaterialsProviderThatRequiresAttributes (CryptographicMaterialsProvider ):
170
+ """Cryptographic materials provider that passes through to another, but requires that attributes are set.
171
+
172
+ If the EncryptionContext passed to decryption_materials or encryption_materials
173
+ ever does not have attributes set,
174
+ a ValueError is raised.
175
+ Otherwise, it passes through to the passthrough CMP normally.
176
+ """
177
+
178
+ def __init__ (self , passthrough_cmp ):
179
+ self ._passthrough_cmp = passthrough_cmp
180
+
181
+ def _assert_attributes_set (self , encryption_context ):
182
+ # type: (EncryptionContext) -> None
183
+ if not encryption_context .attributes :
184
+ raise ValueError ("Encryption context attributes MUST be set!" )
185
+
186
+ def decryption_materials (self , encryption_context ):
187
+ # type: (EncryptionContext) -> CryptographicMaterials
188
+ self ._assert_attributes_set (encryption_context )
189
+ return self ._passthrough_cmp .decryption_materials (encryption_context )
190
+
191
+ def encryption_materials (self , encryption_context ):
192
+ # type: (EncryptionContext) -> CryptographicMaterials
193
+ self ._assert_attributes_set (encryption_context )
194
+ return self ._passthrough_cmp .encryption_materials (encryption_context )
195
+
196
+ def refresh (self ):
197
+ # type: () -> None
198
+ self ._passthrough_cmp .refresh ()
199
+
200
+
167
201
def _get_from_cache (dk_class , algorithm , key_length ):
168
202
"""Don't generate new keys every time. All we care about is that they are valid keys, not that they are unique."""
169
203
try :
@@ -221,8 +255,15 @@ def _some_algorithm_pairs():
221
255
_cmp_builders = {"static" : build_static_jce_cmp , "wrapped" : _build_wrapped_jce_cmp }
222
256
223
257
224
- def _all_possible_cmps (algorithm_generator ):
225
- """Generate all possible cryptographic materials providers based on the supplied generator."""
258
+ def _all_possible_cmps (algorithm_generator , require_attributes ):
259
+ """Generate all possible cryptographic materials providers based on the supplied generator.
260
+
261
+ require_attributes determines whether the CMP will be wrapped in
262
+ PassThroughCryptographicMaterialsProviderThatRequiresAttributes
263
+ to require that attributes are set on every request.
264
+ This should ONLY be disabled on the item encryptor tests.
265
+ All high-level helper clients MUST set the attributes before passing the encryption context down.
266
+ """
226
267
# The AES combinations do the same thing, but this makes sure that the AESWrap name works as expected.
227
268
yield _build_wrapped_jce_cmp ("AESWrap" , 256 , "HmacSHA256" , 256 )
228
269
@@ -242,17 +283,28 @@ def _all_possible_cmps(algorithm_generator):
242
283
sig_key_length = signing_key_length ,
243
284
)
244
285
245
- yield pytest .param (
246
- builder_func (encryption_algorithm , encryption_key_length , signing_algorithm , signing_key_length ),
247
- id = id_string ,
248
- )
286
+ inner_cmp = builder_func (encryption_algorithm , encryption_key_length , signing_algorithm , signing_key_length )
287
+
288
+ if require_attributes :
289
+ outer_cmp = PassThroughCryptographicMaterialsProviderThatRequiresAttributes (inner_cmp )
290
+ else :
291
+ outer_cmp = inner_cmp
292
+
293
+ yield pytest .param (outer_cmp , id = id_string )
249
294
250
295
251
- def set_parametrized_cmp (metafunc ):
252
- """Set paramatrized values for cryptographic materials providers."""
296
+ def set_parametrized_cmp (metafunc , require_attributes = True ):
297
+ """Set paramatrized values for cryptographic materials providers.
298
+
299
+ require_attributes determines whether the CMP will be wrapped in
300
+ PassThroughCryptographicMaterialsProviderThatRequiresAttributes
301
+ to require that attributes are set on every request.
302
+ This should ONLY be disabled on the item encryptor tests.
303
+ All high-level helper clients MUST set the attributes before passing the encryption context down.
304
+ """
253
305
for name , algorithm_generator in (("all_the_cmps" , _all_algorithm_pairs ), ("some_cmps" , _some_algorithm_pairs )):
254
306
if name in metafunc .fixturenames :
255
- metafunc .parametrize (name , _all_possible_cmps (algorithm_generator ))
307
+ metafunc .parametrize (name , _all_possible_cmps (algorithm_generator , require_attributes ))
256
308
257
309
258
310
_ACTIONS = {
@@ -437,30 +489,34 @@ def cycle_batch_item_check(
437
489
check_attribute_actions = initial_actions .copy ()
438
490
check_attribute_actions .set_index_keys (* list (TEST_KEY .keys ()))
439
491
items = _generate_items (initial_item , write_transformer )
492
+ items_in_table = len (items )
440
493
441
494
_put_result = encrypted .batch_write_item ( # noqa
442
495
RequestItems = {table_name : [{"PutRequest" : {"Item" : _item }} for _item in items ]}
443
496
)
444
497
445
- ddb_keys = [write_transformer (key ) for key in TEST_BATCH_KEYS ]
446
- encrypted_result = raw .batch_get_item (RequestItems = {table_name : {"Keys" : ddb_keys }})
447
- check_many_encrypted_items (
448
- actual = encrypted_result ["Responses" ][table_name ],
449
- expected = items ,
450
- attribute_actions = check_attribute_actions ,
451
- transformer = read_transformer ,
452
- )
453
-
454
- decrypted_result = encrypted .batch_get_item (RequestItems = {table_name : {"Keys" : ddb_keys }})
455
- assert_equal_lists_of_items (
456
- actual = decrypted_result ["Responses" ][table_name ], expected = items , transformer = read_transformer
457
- )
498
+ try :
499
+ ddb_keys = [write_transformer (key ) for key in TEST_BATCH_KEYS ]
500
+ encrypted_result = raw .batch_get_item (RequestItems = {table_name : {"Keys" : ddb_keys }})
501
+ check_many_encrypted_items (
502
+ actual = encrypted_result ["Responses" ][table_name ],
503
+ expected = items ,
504
+ attribute_actions = check_attribute_actions ,
505
+ transformer = read_transformer ,
506
+ )
458
507
459
- if delete_items :
460
- _cleanup_items (encrypted , write_transformer , table_name )
508
+ decrypted_result = encrypted .batch_get_item (RequestItems = {table_name : {"Keys" : ddb_keys }})
509
+ assert_equal_lists_of_items (
510
+ actual = decrypted_result ["Responses" ][table_name ], expected = items , transformer = read_transformer
511
+ )
512
+ finally :
513
+ if delete_items :
514
+ _cleanup_items (encrypted , write_transformer , table_name )
515
+ items_in_table = 0
461
516
462
517
del check_attribute_actions
463
518
del items
519
+ return items_in_table
464
520
465
521
466
522
def cycle_batch_writer_check (raw_table , encrypted_table , initial_actions , initial_item ):
@@ -692,16 +748,23 @@ def client_batch_items_unprocessed_check(
692
748
)
693
749
694
750
695
- def client_cycle_batch_items_check_paginators (
751
+ def client_cycle_batch_items_check_scan_paginator (
696
752
materials_provider , initial_actions , initial_item , table_name , region_name = None
697
753
):
754
+ """Helper function for testing the "scan" paginator.
755
+
756
+ Populate the specified table with encrypted items,
757
+ scan the table with raw client paginator to get encrypted items,
758
+ scan the table with encrypted client paginator to get decrypted items,
759
+ then verify that all items appear to have been encrypted correctly.
760
+ """
698
761
kwargs = {}
699
762
if region_name is not None :
700
763
kwargs ["region_name" ] = region_name
701
764
client = boto3 .client ("dynamodb" , ** kwargs )
702
765
e_client = EncryptedClient (client = client , materials_provider = materials_provider , attribute_actions = initial_actions )
703
766
704
- cycle_batch_item_check (
767
+ items_in_table = cycle_batch_item_check (
705
768
raw = client ,
706
769
encrypted = e_client ,
707
770
initial_actions = initial_actions ,
@@ -712,29 +775,31 @@ def client_cycle_batch_items_check_paginators(
712
775
delete_items = False ,
713
776
)
714
777
715
- encrypted_items = []
716
- raw_paginator = client .get_paginator ("scan" )
717
- for page in raw_paginator .paginate (TableName = table_name , ConsistentRead = True ):
718
- encrypted_items .extend (page ["Items" ])
719
-
720
- decrypted_items = []
721
- encrypted_paginator = e_client .get_paginator ("scan" )
722
- for page in encrypted_paginator .paginate (TableName = table_name , ConsistentRead = True ):
723
- decrypted_items .extend (page ["Items" ])
724
-
725
- print (encrypted_items )
726
- print (decrypted_items )
727
-
728
- check_attribute_actions = initial_actions .copy ()
729
- check_attribute_actions .set_index_keys (* list (TEST_KEY .keys ()))
730
- check_many_encrypted_items (
731
- actual = encrypted_items ,
732
- expected = decrypted_items ,
733
- attribute_actions = check_attribute_actions ,
734
- transformer = ddb_to_dict ,
735
- )
778
+ try :
779
+ encrypted_items = []
780
+ raw_paginator = client .get_paginator ("scan" )
781
+ for page in raw_paginator .paginate (TableName = table_name , ConsistentRead = True ):
782
+ encrypted_items .extend (page ["Items" ])
783
+
784
+ decrypted_items = []
785
+ encrypted_paginator = e_client .get_paginator ("scan" )
786
+ for page in encrypted_paginator .paginate (TableName = table_name , ConsistentRead = True ):
787
+ decrypted_items .extend (page ["Items" ])
788
+
789
+ assert encrypted_items and decrypted_items
790
+ assert len (encrypted_items ) == len (decrypted_items ) == items_in_table
791
+
792
+ check_attribute_actions = initial_actions .copy ()
793
+ check_attribute_actions .set_index_keys (* list (TEST_KEY .keys ()))
794
+ check_many_encrypted_items (
795
+ actual = encrypted_items ,
796
+ expected = decrypted_items ,
797
+ attribute_actions = check_attribute_actions ,
798
+ transformer = ddb_to_dict ,
799
+ )
736
800
737
- _cleanup_items (encrypted = e_client , write_transformer = dict_to_ddb , table_name = table_name )
801
+ finally :
802
+ _cleanup_items (encrypted = e_client , write_transformer = dict_to_ddb , table_name = table_name )
738
803
739
804
raw_scan_result = client .scan (TableName = table_name , ConsistentRead = True )
740
805
e_scan_result = e_client .scan (TableName = table_name , ConsistentRead = True )
0 commit comments