Skip to content

Commit db45578

Browse files
auto commit
1 parent b373067 commit db45578

File tree

6 files changed

+388
-0
lines changed

6 files changed

+388
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Amazon.DynamoDBv2.Model;
4+
5+
namespace Examples.migration.PlaintextToAWSDBE
6+
{
7+
/*
8+
Utility class for the PlaintextToAWSDBE migration examples.
9+
This class contains shared functionality used by all migration steps.
10+
*/
11+
public class MigrationUtils
12+
{
13+
// Verify that a returned item matches the expected values
14+
public static bool VerifyReturnedItem(GetItemResponse response, string partitionKeyValue, string sortKeyValue,
15+
string encryptedAndSignedValue, string signOnlyValue, string doNothingValue)
16+
{
17+
var item = response.Item;
18+
19+
// Verify partition key
20+
if (!item.ContainsKey("partition_key") || item["partition_key"].S != partitionKeyValue)
21+
{
22+
throw new Exception($"partition_key mismatch: expected {partitionKeyValue}, got {(item.ContainsKey("partition_key") ? item["partition_key"].S : "null")}");
23+
}
24+
25+
// Verify sort key
26+
if (!item.ContainsKey("sort_key") || item["sort_key"].N != sortKeyValue)
27+
{
28+
throw new Exception($"sort_key mismatch: expected {sortKeyValue}, got {(item.ContainsKey("sort_key") ? item["sort_key"].N : "null")}");
29+
}
30+
31+
// Verify attribute1 (will be encrypted and signed in future steps)
32+
if (!item.ContainsKey("attribute1") || item["attribute1"].S != encryptedAndSignedValue)
33+
{
34+
throw new Exception($"attribute1 mismatch: expected {encryptedAndSignedValue}, got {(item.ContainsKey("attribute1") ? item["attribute1"].S : "null")}");
35+
}
36+
37+
// Verify attribute2 (will be signed but not encrypted in future steps)
38+
if (!item.ContainsKey("attribute2") || item["attribute2"].S != signOnlyValue)
39+
{
40+
throw new Exception($"attribute2 mismatch: expected {signOnlyValue}, got {(item.ContainsKey("attribute2") ? item["attribute2"].S : "null")}");
41+
}
42+
43+
// Verify attribute3 (will neither be encrypted nor signed in future steps)
44+
if (!item.ContainsKey("attribute3") || item["attribute3"].S != doNothingValue)
45+
{
46+
throw new Exception($"attribute3 mismatch: expected {doNothingValue}, got {(item.ContainsKey("attribute3") ? item["attribute3"].S : "null")}");
47+
}
48+
49+
return true;
50+
}
51+
52+
// Helper method to clean up test items
53+
public static async System.Threading.Tasks.Task CleanupItems(string tableName, string partitionKey, string[] sortKeys)
54+
{
55+
var ddb = new Amazon.DynamoDBv2.AmazonDynamoDBClient();
56+
57+
foreach (var sortKey in sortKeys)
58+
{
59+
try
60+
{
61+
var key = new Dictionary<string, AttributeValue>
62+
{
63+
["partition_key"] = new AttributeValue { S = partitionKey },
64+
["sort_key"] = new AttributeValue { N = sortKey }
65+
};
66+
67+
var deleteRequest = new DeleteItemRequest
68+
{
69+
TableName = tableName,
70+
Key = key
71+
};
72+
73+
await ddb.DeleteItemAsync(deleteRequest);
74+
}
75+
catch (Exception e)
76+
{
77+
// Log but don't fail if cleanup fails
78+
Console.WriteLine($"Warning: Failed to clean up test item with sort key {sortKey}: {e.Message}");
79+
}
80+
}
81+
}
82+
}
83+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Amazon.DynamoDBv2;
5+
using Amazon.DynamoDBv2.Model;
6+
using System.Diagnostics;
7+
using System.Net;
8+
using AWS.Cryptography.DbEncryptionSDK.DynamoDb;
9+
using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption;
10+
using AWS.Cryptography.MaterialProviders;
11+
using Examples.migration.PlaintextToAWSDBE;
12+
13+
namespace Examples.migration.PlaintextToAWSDBE.awsdbe
14+
{
15+
/*
16+
Migration Step 1: This is the first step in the migration process from
17+
plaintext to encrypted DynamoDB using the AWS Database Encryption SDK.
18+
19+
This step will be implemented in the future. It will demonstrate how to:
20+
1. Configure a DynamoDB client with encryption capabilities
21+
2. Write items that can still be read by plaintext clients (Step 0)
22+
3. Read both plaintext items and items written by this step
23+
24+
Running this example requires access to the DDB Table whose name
25+
is provided in the function parameter.
26+
This table must be configured with the following
27+
primary key configuration:
28+
- Partition key is named "partition_key" with type (S)
29+
- Sort key is named "sort_key" with type (S)
30+
*/
31+
public class MigrationStep1
32+
{
33+
// This method will be implemented in the future
34+
public static async Task<bool> MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue)
35+
{
36+
// TODO: Implement MigrationStep1
37+
throw new NotImplementedException("MigrationStep1 is not yet implemented");
38+
}
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Amazon.DynamoDBv2;
5+
using Amazon.DynamoDBv2.Model;
6+
using System.Diagnostics;
7+
using System.Net;
8+
using AWS.Cryptography.DbEncryptionSDK.DynamoDb;
9+
using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption;
10+
using AWS.Cryptography.MaterialProviders;
11+
using Examples.migration.PlaintextToAWSDBE;
12+
13+
namespace Examples.migration.PlaintextToAWSDBE.awsdbe
14+
{
15+
/*
16+
Migration Step 2: This is the second step in the migration process from
17+
plaintext to encrypted DynamoDB using the AWS Database Encryption SDK.
18+
19+
This step will be implemented in the future. It will demonstrate how to:
20+
1. Configure a DynamoDB client with full encryption capabilities
21+
2. Write fully encrypted items that cannot be read by plaintext clients (Step 0)
22+
3. Read both plaintext items and fully encrypted items
23+
24+
Running this example requires access to the DDB Table whose name
25+
is provided in the function parameter.
26+
This table must be configured with the following
27+
primary key configuration:
28+
- Partition key is named "partition_key" with type (S)
29+
- Sort key is named "sort_key" with type (S)
30+
*/
31+
public class MigrationStep2
32+
{
33+
// This method will be implemented in the future
34+
public static async Task<bool> MigrationStep2Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue)
35+
{
36+
// TODO: Implement MigrationStep2
37+
throw new NotImplementedException("MigrationStep2 is not yet implemented");
38+
}
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Amazon.DynamoDBv2;
5+
using Amazon.DynamoDBv2.Model;
6+
using System.Diagnostics;
7+
using System.Net;
8+
using AWS.Cryptography.DbEncryptionSDK.DynamoDb;
9+
using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption;
10+
using AWS.Cryptography.MaterialProviders;
11+
using Examples.migration.PlaintextToAWSDBE;
12+
13+
namespace Examples.migration.PlaintextToAWSDBE.awsdbe
14+
{
15+
/*
16+
Migration Step 3: This is the final step in the migration process from
17+
plaintext to encrypted DynamoDB using the AWS Database Encryption SDK.
18+
19+
This step will be implemented in the future. It will demonstrate how to:
20+
1. Configure a DynamoDB client with full encryption capabilities
21+
2. Write fully encrypted items with additional security features
22+
3. Read both plaintext items and fully encrypted items
23+
24+
Running this example requires access to the DDB Table whose name
25+
is provided in the function parameter.
26+
This table must be configured with the following
27+
primary key configuration:
28+
- Partition key is named "partition_key" with type (S)
29+
- Sort key is named "sort_key" with type (S)
30+
*/
31+
public class MigrationStep3
32+
{
33+
// This method will be implemented in the future
34+
public static async Task<bool> MigrationStep3Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue)
35+
{
36+
// TODO: Implement MigrationStep3
37+
throw new NotImplementedException("MigrationStep3 is not yet implemented");
38+
}
39+
}
40+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Amazon.DynamoDBv2;
5+
using Amazon.DynamoDBv2.Model;
6+
using System.Diagnostics;
7+
using System.Net;
8+
using Examples.migration.PlaintextToAWSDBE;
9+
10+
namespace Examples.migration.PlaintextToAWSDBE.plaintext
11+
{
12+
/*
13+
Migration Step 0: This is the pre-migration step for the
14+
plaintext-to-encrypted database migration, and is the starting
15+
state for our migration from a plaintext database to a
16+
client-side encrypted database encrypted using the
17+
AWS Database Encryption SDK for DynamoDb.
18+
19+
In this example, we configure a DynamoDbClient to
20+
write a plaintext record to a table and read that record.
21+
This emulates the starting state of a plaintext-to-encrypted
22+
database migration; i.e. a plaintext database you can
23+
read and write to with the DynamoDbClient.
24+
25+
Running this example requires access to the DDB Table whose name
26+
is provided in the function parameter.
27+
This table must be configured with the following
28+
primary key configuration:
29+
- Partition key is named "partition_key" with type (S)
30+
- Sort key is named "sort_key" with type (S)
31+
*/
32+
public class MigrationStep0
33+
{
34+
public static async Task<bool> MigrationStep0Example(string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue)
35+
{
36+
try
37+
{
38+
// 1. Create a standard DynamoDB client
39+
var ddb = new AmazonDynamoDBClient();
40+
41+
// 2. Put an example item into DynamoDB table
42+
// This item will be stored in plaintext.
43+
string encryptedAndSignedValue = "this will be encrypted and signed";
44+
string signOnlyValue = "this will never be encrypted, but it will be signed";
45+
string doNothingValue = "this will never be encrypted nor signed";
46+
var item = new Dictionary<string, AttributeValue>
47+
{
48+
["partition_key"] = new AttributeValue { S = partitionKeyValue },
49+
["sort_key"] = new AttributeValue { N = sortKeyWriteValue },
50+
["attribute1"] = new AttributeValue { S = encryptedAndSignedValue },
51+
["attribute2"] = new AttributeValue { S = signOnlyValue },
52+
["attribute3"] = new AttributeValue { S = doNothingValue }
53+
};
54+
55+
var putRequest = new PutItemRequest
56+
{
57+
TableName = ddbTableName,
58+
Item = item
59+
};
60+
61+
var putResponse = await ddb.PutItemAsync(putRequest);
62+
Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK);
63+
64+
// 3. Get an item back from the table as it was written.
65+
// If this is an item written in plaintext (i.e. any item written
66+
// during Step 0 or 1), then the item will still be in plaintext
67+
// and will be able to be processed.
68+
// If this is an item that was encrypted client-side (i.e. any item written
69+
// during Step 2 or after), then the item will still be encrypted client-side
70+
// and will be unable to be processed in your code. To decrypt and process
71+
// client-side encrypted items, you will need to configure encrypted reads on
72+
// your dynamodb client (this is configured from Step 1 onwards).
73+
var key = new Dictionary<string, AttributeValue>
74+
{
75+
["partition_key"] = new AttributeValue { S = partitionKeyValue },
76+
["sort_key"] = new AttributeValue { N = sortKeyReadValue }
77+
};
78+
79+
var getRequest = new GetItemRequest
80+
{
81+
TableName = ddbTableName,
82+
Key = key
83+
};
84+
85+
var getResponse = await ddb.GetItemAsync(getRequest);
86+
Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK);
87+
88+
// 4. Verify we get the expected item back
89+
if (getResponse.Item == null)
90+
{
91+
throw new Exception("No item found");
92+
}
93+
94+
bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue, encryptedAndSignedValue, signOnlyValue, doNothingValue);
95+
if (success)
96+
{
97+
Console.WriteLine("MigrationStep0 completed successfully");
98+
}
99+
return success;
100+
}
101+
catch (Exception e)
102+
{
103+
Console.WriteLine($"Error in MigrationStep0: {e.Message}");
104+
throw;
105+
}
106+
}
107+
108+
}
109+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Amazon.DynamoDBv2;
5+
using Amazon.DynamoDBv2.Model;
6+
using System.Diagnostics;
7+
using Xunit;
8+
using Examples.migration.PlaintextToAWSDBE;
9+
10+
namespace Examples.migration.PlaintextToAWSDBE.plaintext
11+
{
12+
/*
13+
Test for Migration Step 0: This tests the pre-migration step for the
14+
plaintext-to-encrypted database migration.
15+
16+
This test verifies that:
17+
1. Step 0 can successfully write and read plaintext items
18+
2. (Future) Step 0 can read items written by Step 1 (commented out until Step 1 is implemented)
19+
3. (Future) Step 0 cannot read items written by Steps 2 and 3 (commented out until Steps 2 and 3 are implemented)
20+
21+
Running this test requires access to the DDB Table whose name
22+
is provided by TestUtils.TEST_DDB_TABLE_NAME.
23+
This table must be configured with the following
24+
primary key configuration:
25+
- Partition key is named "partition_key" with type (S)
26+
- Sort key is named "sort_key" with type (S)
27+
*/
28+
public class MigrationStep0Test
29+
{
30+
[Fact]
31+
public async Task TestMigrationStep0()
32+
{
33+
string kmsKeyID = TestUtils.TEST_KMS_KEY_ID;
34+
string tableName = TestUtils.TEST_DDB_TABLE_NAME;
35+
string partitionKey = Guid.NewGuid().ToString();
36+
string[] sortKeys = { "0", "1", "2", "3" };
37+
38+
try
39+
{
40+
// Successfully executes step 0
41+
bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]);
42+
Assert.True(success, "MigrationStep0 should complete successfully");
43+
44+
// The following tests will be implemented once the other migration steps are created
45+
46+
// Given: Step 1 has succeeded
47+
// await awsdbe.MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]);
48+
49+
// When: Execute Step 0 with sortReadValue=1, Then: Success (i.e. can read plaintext values)
50+
// success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[1]);
51+
// Assert.True(success, "MigrationStep0 should be able to read items written by Step 1");
52+
53+
// Given: Step 2 has succeeded
54+
// await awsdbe.MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]);
55+
56+
// When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items.
57+
// var exception = await Assert.ThrowsAsync<Exception>(() =>
58+
// MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[2]));
59+
// Assert.Contains("attribute1 is not a string attribute", exception.Message);
60+
61+
// Given: Step 3 has succeeded (if it exists)
62+
// await awsdbe.MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]);
63+
64+
// When: Execute Step 0 with sortReadValue=3, Then: should error out
65+
// exception = await Assert.ThrowsAsync<Exception>(() =>
66+
// MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[3]));
67+
// Assert.Contains("attribute1 is not a string attribute", exception.Message);
68+
}
69+
finally
70+
{
71+
// Cleanup
72+
await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys);
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)