1+ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+ // SPDX-License-Identifier: Apache-2.0
3+
4+ use aws_sdk_dynamodb:: types:: AttributeValue ;
5+ use std:: collections:: HashMap ;
6+ use aws_db_esdk:: intercept:: DbEsdkInterceptor ;
7+ use aws_db_esdk:: dynamodb:: types:: PlaintextOverride ;
8+ use crate :: migration:: plaintext_to_awsdbe:: migration_utils:: {
9+ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE , SIGN_ONLY_VALUE , DO_NOTHING_VALUE ,
10+ } ;
11+ use crate :: migration:: plaintext_to_awsdbe:: awsdbe:: common:: create_table_configs;
12+
13+ /*
14+ Migration Step 1: This is the first step in the migration process from
15+ plaintext to encrypted DynamoDB using the AWS Database Encryption SDK.
16+
17+ In this example, we configure a DynamoDB Encryption client to do the following:
18+ 1. Write items only in plaintext
19+ 2. Read items in plaintext or, if the item is encrypted, decrypt with our encryption configuration
20+
21+ While this step configures your client to be ready to start reading encrypted items,
22+ we do not yet expect to be reading any encrypted items,
23+ as our client still writes plaintext items.
24+ Before you move on to step 2, ensure that these changes have successfully been deployed
25+ to all of your readers.
26+
27+ Running this example requires access to the DDB Table whose name
28+ is provided in the function parameter.
29+ This table must be configured with the following
30+ primary key configuration:
31+ - Partition key is named "partition_key" with type (S)
32+ - Sort key is named "sort_key" with type (N)
33+ */
34+ pub async fn migration_step_1_example (
35+ kms_key_id : & str ,
36+ ddb_table_name : & str ,
37+ partition_key_value : & str ,
38+ sort_key_write_value : & str ,
39+ sort_key_read_value : & str ,
40+ ) -> Result < bool , Box < dyn std:: error:: Error > > {
41+ // 1. Create table configurations
42+ // In this step of migration we will use PlaintextOverride::ForcePlaintextWriteAllowPlaintextRead
43+ // which means:
44+ // - Write: Items are forced to be written as plaintext.
45+ // Items may not be written as encrypted items.
46+ // - Read: Items are allowed to be read as plaintext.
47+ // Items are allowed to be read as encrypted items.
48+ let table_configs = create_table_configs (
49+ kms_key_id,
50+ ddb_table_name,
51+ PlaintextOverride :: ForcePlaintextWriteAllowPlaintextRead ,
52+ )
53+ . await ?;
54+
55+ // 2. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
56+ let sdk_config = aws_config:: load_defaults ( aws_config:: BehaviorVersion :: latest ( ) ) . await ;
57+ let dynamo_config = aws_sdk_dynamodb:: config:: Builder :: from ( & sdk_config)
58+ . interceptor ( DbEsdkInterceptor :: new ( table_configs) ?)
59+ . build ( ) ;
60+ let ddb = aws_sdk_dynamodb:: Client :: from_conf ( dynamo_config) ;
61+
62+ // 3. Put an item into our table using the above client.
63+ // This item will be stored in plaintext due to our PlaintextOverride configuration.
64+ let partition_key_name = "partition_key" ;
65+ let sort_key_name = "sort_key" ;
66+ let encrypted_and_signed_value = ENCRYPTED_AND_SIGNED_VALUE ;
67+ let sign_only_value = SIGN_ONLY_VALUE ;
68+ let do_nothing_value = DO_NOTHING_VALUE ;
69+ let item = HashMap :: from ( [
70+ (
71+ partition_key_name. to_string ( ) ,
72+ AttributeValue :: S ( partition_key_value. to_string ( ) ) ,
73+ ) ,
74+ (
75+ sort_key_name. to_string ( ) ,
76+ AttributeValue :: N ( sort_key_write_value. to_string ( ) ) ,
77+ ) ,
78+ (
79+ "attribute1" . to_string ( ) ,
80+ AttributeValue :: S ( encrypted_and_signed_value. to_string ( ) ) ,
81+ ) ,
82+ (
83+ "attribute2" . to_string ( ) ,
84+ AttributeValue :: S ( sign_only_value. to_string ( ) ) ,
85+ ) ,
86+ (
87+ "attribute3" . to_string ( ) ,
88+ AttributeValue :: S ( do_nothing_value. to_string ( ) ) ,
89+ ) ,
90+ ] ) ;
91+
92+ ddb. put_item ( )
93+ . table_name ( ddb_table_name)
94+ . set_item ( Some ( item) )
95+ . send ( )
96+ . await ?;
97+
98+ // 4. Get an item back from the table using the same client.
99+ // If this is an item written in plaintext (i.e. any item written
100+ // during Step 0 or 1), then the item will still be in plaintext.
101+ // If this is an item that was encrypted client-side (i.e. any item written
102+ // during Step 2 or after), then the item will be decrypted client-side
103+ // and surfaced as a plaintext item.
104+ let key = HashMap :: from ( [
105+ (
106+ partition_key_name. to_string ( ) ,
107+ AttributeValue :: S ( partition_key_value. to_string ( ) ) ,
108+ ) ,
109+ (
110+ sort_key_name. to_string ( ) ,
111+ AttributeValue :: N ( sort_key_read_value. to_string ( ) ) ,
112+ ) ,
113+ ] ) ;
114+
115+ let response = ddb
116+ . get_item ( )
117+ . table_name ( ddb_table_name)
118+ . set_key ( Some ( key) )
119+ // In this example we configure a strongly consistent read
120+ // because we perform a read immediately after a write (for demonstrative purposes).
121+ // By default, reads are only eventually consistent.
122+ . consistent_read ( true )
123+ . send ( )
124+ . await ?;
125+
126+ // 5. Verify we get the expected item back
127+ if let Some ( item) = response. item {
128+ let success = verify_returned_item ( & item, partition_key_value, sort_key_read_value) ?;
129+ if success {
130+ println ! ( "MigrationStep1 completed successfully" ) ;
131+ }
132+ Ok ( success)
133+ } else {
134+ Err ( "No item found" . into ( ) )
135+ }
136+ }
137+
138+ #[ tokio:: test( flavor = "multi_thread" ) ]
139+ async fn test_migration_step_1 ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
140+ use crate :: migration:: plaintext_to_awsdbe:: plaintext:: migration_step_0:: migration_step_0_example;
141+ use crate :: migration:: plaintext_to_awsdbe:: awsdbe:: migration_step_2:: migration_step_2_example;
142+ use crate :: migration:: plaintext_to_awsdbe:: awsdbe:: migration_step_3:: migration_step_3_example;
143+ use crate :: test_utils;
144+ use uuid:: Uuid ;
145+
146+ let kms_key_id = test_utils:: TEST_KMS_KEY_ID ;
147+ let table_name = test_utils:: TEST_DDB_TABLE_NAME ;
148+ let partition_key = Uuid :: new_v4 ( ) . to_string ( ) ;
149+ let sort_keys = [ "0" , "1" , "2" , "3" ] ;
150+
151+ // Successfully executes step 1
152+ let success = migration_step_1_example ( kms_key_id, table_name, & partition_key, sort_keys[ 1 ] , sort_keys[ 1 ] ) . await ?;
153+ assert ! ( success, "MigrationStep1 should complete successfully" ) ;
154+
155+ // Given: Step 0 has succeeded
156+ let success = migration_step_0_example ( table_name, & partition_key, sort_keys[ 0 ] , sort_keys[ 0 ] ) . await ?;
157+ assert ! ( success, "MigrationStep0 should complete successfully" ) ;
158+
159+ // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0)
160+ let success = migration_step_1_example ( kms_key_id, table_name, & partition_key, sort_keys[ 1 ] , sort_keys[ 0 ] ) . await ?;
161+ assert ! ( success, "MigrationStep1 should be able to read items written by Step 0" ) ;
162+
163+ // Given: Step 2 has succeeded
164+ let success = migration_step_2_example ( kms_key_id, table_name, & partition_key, sort_keys[ 2 ] , sort_keys[ 2 ] ) . await ?;
165+ assert ! ( success, "MigrationStep2 should complete successfully" ) ;
166+
167+ // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2)
168+ let success = migration_step_1_example ( kms_key_id, table_name, & partition_key, sort_keys[ 1 ] , sort_keys[ 2 ] ) . await ?;
169+ assert ! ( success, "MigrationStep1 should be able to read items written by Step 2" ) ;
170+
171+ // Given: Step 3 has succeeded
172+ let success = migration_step_3_example ( kms_key_id, table_name, & partition_key, sort_keys[ 3 ] , sort_keys[ 3 ] ) . await ?;
173+ assert ! ( success, "MigrationStep3 should complete successfully" ) ;
174+
175+ // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3)
176+ let success = migration_step_1_example ( kms_key_id, table_name, & partition_key, sort_keys[ 1 ] , sort_keys[ 3 ] ) . await ?;
177+ assert ! ( success, "MigrationStep1 should be able to read items written by Step 3" ) ;
178+
179+ // Cleanup
180+ for sort_key in & sort_keys {
181+ test_utils:: cleanup_items ( table_name, & partition_key, sort_key) . await ?;
182+ }
183+
184+ Ok ( ( ) )
185+ }
0 commit comments