Skip to content

Commit eaad705

Browse files
committed
migration examples for Encrypted Paginator
1 parent f14737d commit eaad705

File tree

12 files changed

+617
-0
lines changed

12 files changed

+617
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Stub to allow relative imports of examples from tests."""
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Migration Step 1.
6+
7+
This is an example demonstrating how to start using the
8+
AWS Database Encryption SDK with a pre-existing table used with DynamoDB Encryption Client.
9+
In this example, you configure a EncryptedPaginator to do the following:
10+
- Read items encrypted in the old format
11+
- Continue to encrypt items in the old format on write
12+
- Read items encrypted in the new format
13+
While this step configures your paginator to be ready to start reading items encrypted,
14+
we do not yet expect to be reading any items in the new format.
15+
Before you move on to step 2, ensure that these changes have successfully been deployed
16+
to all of your readers.
17+
18+
Running this example requires access to the DDB Table whose name
19+
is provided in CLI arguments.
20+
This table must be configured with the following
21+
primary key configuration:
22+
- Partition key is named "partition_key" with type (S)
23+
- Sort key is named "sort_key" with type (N)
24+
"""
25+
from aws_dbesdk_dynamodb.structures.dynamodb import LegacyPolicy
26+
27+
from ..client.common import setup_awsdbe_client_with_legacy_override
28+
29+
30+
def migration_step_1_with_paginator(kms_key_id: str, ddb_table_name: str, sort_read_value: int = 1):
31+
"""
32+
Migration Step 1: Using the AWS Database Encryption SDK EncryptedPaginator with Legacy Override.
33+
34+
:param kms_key_id: The ARN of the KMS key to use for encryption
35+
:param ddb_table_name: The name of the DynamoDB table
36+
"""
37+
# 1. Create a EncryptedClient with legacy override.
38+
# For Legacy Policy, use `FORCE_LEGACY_ENCRYPT_ALLOW_LEGACY_DECRYPT`.
39+
# With this policy, you will continue to read and write items using the old format,
40+
# but will be able to start reading new items in the new format as soon as they appear
41+
policy = LegacyPolicy.FORCE_LEGACY_ENCRYPT_ALLOW_LEGACY_DECRYPT
42+
encrypted_client = setup_awsdbe_client_with_legacy_override(
43+
kms_key_id=kms_key_id, ddb_table_name=ddb_table_name, policy=policy
44+
)
45+
46+
# 2. Put an item in the old format since we are using a legacy override
47+
# with FORCE_LEGACY_ENCRYPT_ALLOW_DECRYPT policy
48+
item_to_encrypt = {
49+
"partition_key": {"S": "PaginatorMigrationExampleForPython"},
50+
"sort_key": {"N": "1"},
51+
"attribute1": {"S": "encrypt and sign me!"},
52+
"attribute2": {"S": "sign me!"},
53+
":attribute3": {"S": "ignore me!"},
54+
}
55+
56+
put_item_request = {
57+
"TableName": ddb_table_name,
58+
"Item": item_to_encrypt,
59+
}
60+
61+
put_item_response = encrypted_client.put_item(**put_item_request)
62+
# Demonstrate that PutItem succeeded
63+
assert put_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
64+
65+
# 3. Get the EncryptedPaginator from the EncryptedClient
66+
encrypted_paginator = encrypted_client.get_paginator("query")
67+
68+
# 4. Use the EncryptedPaginator to paginate through the items in the table
69+
# If the items were written in the old format (e.g. any item written
70+
# during Step 0 or 1), then we will attempt to decrypt the item
71+
# using the legacy behavior.
72+
# If the items were written in the new format (e.g. any item written
73+
# during Step 2 or after), then we will attempt to decrypt the item using
74+
# the non-legacy behavior.
75+
items = []
76+
for page in encrypted_paginator.paginate(
77+
TableName=ddb_table_name,
78+
KeyConditionExpression="partition_key = :partition_key AND sort_key = :sort_key",
79+
ExpressionAttributeValues={
80+
":partition_key": {"S": "PaginatorMigrationExampleForPython"},
81+
":sort_key": {"N": str(sort_read_value)},
82+
},
83+
):
84+
for item in page["Items"]:
85+
items.append(item)
86+
87+
# 5. Verify the decrypted items
88+
assert len(items) == 1 # We should have only one item with above key condition
89+
item = next((i for i in items if i["sort_key"]["N"] == str(sort_read_value)), None)
90+
assert item is not None
91+
assert item["partition_key"]["S"] == "PaginatorMigrationExampleForPython"
92+
assert item["attribute1"]["S"] == "encrypt and sign me!"
93+
assert item["attribute2"]["S"] == "sign me!"
94+
assert item[":attribute3"]["S"] == "ignore me!"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Migration Step 2.
6+
7+
This is an example demonstrating how to update your configuration
8+
to start writing items using the latest encryption format, but still continue
9+
to read any items written using the old encryption format.
10+
11+
Once you deploy this change to your system, you will have a dataset
12+
containing items in both the old and new format.
13+
Because the changes in Step 1 have been deployed to all our readers,
14+
we can be sure that our entire system is ready to read this new data.
15+
16+
Running this example requires access to the DDB Table whose name
17+
is provided in CLI arguments.
18+
This table must be configured with the following
19+
primary key configuration:
20+
- Partition key is named "partition_key" with type (S)
21+
- Sort key is named "sort_key" with type (N)
22+
"""
23+
24+
from aws_dbesdk_dynamodb.structures.dynamodb import LegacyPolicy
25+
26+
# Import from new AWS Database Encryption SDK
27+
from ..client.common import setup_awsdbe_client_with_legacy_override
28+
29+
30+
def migration_step_2_with_paginator(kms_key_id: str, ddb_table_name: str, sort_read_value: int = 2):
31+
"""
32+
Migration Step 2: Using the AWS Database Encryption SDK EncryptedPaginator with legacy override.
33+
34+
:param kms_key_id: The ARN of the KMS key to use for encryption
35+
:param ddb_table_name: The name of the DynamoDB table
36+
"""
37+
# 1. Create a EncryptedClient with legacy override.
38+
# When configuring our legacy behavior, use `FORBID_LEGACY_ENCRYPT_ALLOW_LEGACY_DECRYPT`.
39+
# With this policy, you will continue to read items in both formats,
40+
# but will only write new items using the new format.
41+
encrypted_client = setup_awsdbe_client_with_legacy_override(
42+
kms_key_id, ddb_table_name, policy=LegacyPolicy.FORBID_LEGACY_ENCRYPT_ALLOW_LEGACY_DECRYPT
43+
)
44+
45+
# 2. Put an item into your table using the EncryptedClient.
46+
# This item will be encrypted in the latest format, using the
47+
# configuration to decide which attribute to encrypt and/or sign.
48+
item_to_encrypt = {
49+
"partition_key": {"S": "PaginatorMigrationExampleForPython"},
50+
"sort_key": {"N": "2"},
51+
"attribute1": {"S": "encrypt and sign me!"},
52+
"attribute2": {"S": "sign me!"},
53+
":attribute3": {"S": "ignore me!"},
54+
}
55+
56+
put_item_request = {
57+
"TableName": ddb_table_name,
58+
"Item": item_to_encrypt,
59+
}
60+
61+
put_item_response = encrypted_client.put_item(**put_item_request)
62+
# Demonstrate that PutItem succeeded
63+
assert put_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
64+
65+
# 3. Get the EncryptedPaginator from the EncryptedClient
66+
encrypted_paginator = encrypted_client.get_paginator("query")
67+
68+
# 4. Use the EncryptedPaginator to paginate through the items in the table
69+
# If the items were written in the old format (e.g. any item written
70+
# during Step 0 or 1), then we will attempt to decrypt the item
71+
# using the legacy behavior.
72+
# If the items were written in the new format (e.g. any item written
73+
# during Step 2 or after), then we will attempt to decrypt the item using
74+
# the non-legacy behavior.
75+
items = []
76+
for page in encrypted_paginator.paginate(
77+
TableName=ddb_table_name,
78+
KeyConditionExpression="partition_key = :partition_key AND sort_key = :sort_key",
79+
ExpressionAttributeValues={
80+
":partition_key": {"S": "PaginatorMigrationExampleForPython"},
81+
":sort_key": {"N": str(sort_read_value)},
82+
},
83+
):
84+
for item in page["Items"]:
85+
items.append(item)
86+
87+
# 5. Verify the decrypted items
88+
assert len(items) == 1 # We should have only one item with above key condition
89+
item = next((i for i in items if i["sort_key"]["N"] == str(sort_read_value)), None)
90+
assert item is not None
91+
assert item["partition_key"]["S"] == "PaginatorMigrationExampleForPython"
92+
assert item["attribute1"]["S"] == "encrypt and sign me!"
93+
assert item["attribute2"]["S"] == "sign me!"
94+
assert item[":attribute3"]["S"] == "ignore me!"
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Migration Step 3.
6+
7+
This is an example demonstrating how to update your configuration
8+
to stop accepting reading items encrypted using the old format.
9+
In order to proceed with this step, you will need to re-encrypt all
10+
old items in your table.
11+
12+
Once you complete Step 3, you can be sure that all items being read by your system
13+
ensure the security properties configured for the new format.
14+
15+
Running this example requires access to the DDB Table whose name
16+
is provided in CLI arguments.
17+
This table must be configured with the following
18+
primary key configuration:
19+
- Partition key is named "partition_key" with type (S)
20+
- Sort key is named "sort_key" with type (N)
21+
"""
22+
23+
from ..client.common import setup_pure_awsdbe_client
24+
25+
26+
def migration_step_3_with_paginator(kms_key_id: str, ddb_table_name: str, sort_read_value: int = 3):
27+
"""
28+
Migration Step 3: Using only pure AWS DBESDK (no legacy override) with EncryptedPaginator.
29+
30+
:param kms_key_id: The ARN of the KMS key to use for encryption
31+
:param ddb_table_name: The name of the DynamoDB table
32+
"""
33+
# 1. Create the EncryptedClient.
34+
# Do not configure any legacy behavior.
35+
encrypted_client = setup_pure_awsdbe_client(kms_key_id, ddb_table_name)
36+
37+
# 2. Put an item into your table using the Client.
38+
# This item will be encrypted in the latest format, using the
39+
# configuration from your modelled class to decide
40+
# which attribute to encrypt and/or sign.
41+
item = {
42+
"partition_key": {"S": "PaginatorMigrationExampleForPython"},
43+
"sort_key": {"N": "3"},
44+
"attribute1": {"S": "encrypt and sign me!"},
45+
"attribute2": {"S": "sign me!"},
46+
":attribute3": {"S": "ignore me!"},
47+
}
48+
49+
put_item_response = encrypted_client.put_item(TableName=ddb_table_name, Item=item)
50+
# Demonstrate that PutItem succeeded
51+
assert put_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
52+
53+
# 3. Get the EncryptedPaginator from the EncryptedClient
54+
encrypted_paginator = encrypted_client.get_paginator("query")
55+
56+
# 4. Use the EncryptedPaginator to paginate through the items in the table
57+
# If the items were written in the old format (e.g. any item written
58+
# during Step 0 or 1), then we will fail to decrypt those items.
59+
# If the items were written in the new format (e.g. any item written
60+
# during Step 2 or 3), then we will attempt to decrypt the item using
61+
# the non-legacy behavior.
62+
items = []
63+
for page in encrypted_paginator.paginate(
64+
TableName=ddb_table_name,
65+
KeyConditionExpression="partition_key = :partition_key AND sort_key = :sort_key",
66+
ExpressionAttributeValues={
67+
":partition_key": {"S": "PaginatorMigrationExampleForPython"},
68+
":sort_key": {"N": str(sort_read_value)},
69+
},
70+
):
71+
for item in page["Items"]:
72+
items.append(item)
73+
74+
# 5. Verify the decrypted items
75+
assert len(items) == 1 # We should have only one item with above key condition
76+
item = next((i for i in items if i["sort_key"]["N"] == str(sort_read_value)), None)
77+
assert item is not None
78+
assert item["partition_key"]["S"] == "PaginatorMigrationExampleForPython"
79+
assert item["attribute1"]["S"] == "encrypt and sign me!"
80+
assert item["attribute2"]["S"] == "sign me!"
81+
assert item[":attribute3"]["S"] == "ignore me!"
82+
83+
# Note: If we tried to query for items with sort_key = 1 or sort_key = 2 that were
84+
# written with the legacy format in previous migration steps and haven't been
85+
# re-encrypted, the operation would fail with a verification exception.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Stub to allow relative imports of examples from tests."""
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Migration Step 0.
6+
7+
This is an example demonstrating use with the DynamoDb Encryption Client.
8+
and is the starting state for our migration to the AWS Database Encryption SDK for DynamoDb.
9+
In this example we configure an EncryptedClient which provides an encrypted paginator
10+
configured to encrypt and decrypt items. The encryption and decryption of data is
11+
configured to use a KMS Key as the root of trust.
12+
13+
Running this example requires access to the DDB Table whose name
14+
is provided in CLI arguments.
15+
This table must be configured with the following
16+
primary key configuration:
17+
- Partition key is named "partition_key" with type (S)
18+
- Sort key is named "sort_key" with type (N)
19+
"""
20+
21+
import boto3
22+
23+
# Import from legacy DynamoDB Encryption Client
24+
from dynamodb_encryption_sdk.encrypted.client import EncryptedClient
25+
from dynamodb_encryption_sdk.identifiers import CryptoAction
26+
from dynamodb_encryption_sdk.material_providers.aws_kms import AwsKmsCryptographicMaterialsProvider
27+
from dynamodb_encryption_sdk.structures import AttributeActions
28+
29+
30+
def migration_step_0_with_paginator(kms_key_id: str, ddb_table_name: str, sort_read_value: int = 0):
31+
"""
32+
Migration Step 0: Using the DynamoDb Encryption Client with EncryptedClient's paginator.
33+
34+
:param kms_key_id: The ARN of the KMS key to use for encryption
35+
:param ddb_table_name: The name of the DynamoDB table
36+
"""
37+
# 1. Create the MaterialProvider that protects your data keys. For this example,
38+
# we create a KmsCryptographicMaterialsProvider which protects data keys using a single kmsKey.
39+
cmp = AwsKmsCryptographicMaterialsProvider(key_id=kms_key_id)
40+
41+
# 2. Create the AttributeActions to configure encryption and signing
42+
actions = AttributeActions(
43+
default_action=CryptoAction.ENCRYPT_AND_SIGN,
44+
attribute_actions={
45+
"partition_key": CryptoAction.SIGN_ONLY,
46+
"sort_key": CryptoAction.SIGN_ONLY,
47+
"attribute1": CryptoAction.ENCRYPT_AND_SIGN,
48+
"attribute2": CryptoAction.SIGN_ONLY,
49+
":attribute3": CryptoAction.DO_NOTHING,
50+
},
51+
)
52+
53+
# 3. Create a legacy EncryptedClient.
54+
ddb_client = boto3.client("dynamodb")
55+
encrypted_client = EncryptedClient(client=ddb_client, materials_provider=cmp, attribute_actions=actions)
56+
57+
# 4. Put an example item into our DynamoDb table.
58+
# This item will be encrypted client-side before it is sent to DynamoDb.
59+
item = {
60+
"partition_key": {"S": "PaginatorMigrationExampleForPython"},
61+
"sort_key": {"N": "0"},
62+
"attribute1": {"S": "encrypt and sign me!"},
63+
"attribute2": {"S": "sign me!"},
64+
":attribute3": {"S": "ignore me!"},
65+
}
66+
67+
put_item_response = encrypted_client.put_item(TableName=ddb_table_name, Item=item)
68+
# Demonstrate that PutItem succeeded
69+
assert put_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
70+
71+
# 5. Get a paginator from the encrypted client
72+
# The paginator will automatically decrypt items as they are returned.
73+
encrypted_paginator = encrypted_client.get_paginator("query")
74+
75+
# 6. Use the paginator to get items from the table
76+
items = []
77+
for page in encrypted_paginator.paginate(
78+
TableName=ddb_table_name,
79+
KeyConditionExpression="partition_key = :partition_key AND sort_key = :sort_key",
80+
ExpressionAttributeValues={
81+
":partition_key": {"S": "PaginatorMigrationExampleForPython"},
82+
":sort_key": {"N": str(sort_read_value)},
83+
},
84+
):
85+
for item in page["Items"]:
86+
items.append(item)
87+
88+
# 7. Verify the decrypted items
89+
assert len(items) == 1 # We should have only one item with above key condition
90+
item = next((i for i in items if i["sort_key"]["N"] == str(sort_read_value)), None)
91+
assert item is not None
92+
assert item["partition_key"]["S"] == "PaginatorMigrationExampleForPython"
93+
assert item["attribute1"]["S"] == "encrypt and sign me!"
94+
assert item["attribute2"]["S"] == "sign me!"
95+
assert item[":attribute3"]["S"] == "ignore me!"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Stub to allow relative imports of examples from tests."""

0 commit comments

Comments
 (0)