Skip to content

test(examples): add example for DDB Global Table v2017 #164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion codebuild/corretto11.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ phases:
java: corretto11
build:
commands:
- mvn install
- mvn install --no-transfer-progress
2 changes: 1 addition & 1 deletion codebuild/corretto8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ phases:
java: corretto8
build:
commands:
- mvn install
- mvn install --no-transfer-progress
2 changes: 1 addition & 1 deletion codebuild/openjdk11.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ phases:
java: openjdk11
build:
commands:
- mvn install
- mvn install --no-transfer-progress
2 changes: 1 addition & 1 deletion codebuild/openjdk8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ phases:
java: openjdk8
build:
commands:
- mvn install
- mvn install --no-transfer-progress
3 changes: 2 additions & 1 deletion codebuild/release/release-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ phases:
-Dgpg.passphrase="$GPG_PASS" \
-Dsonatype.username="$SONA_USERNAME" \
-Dsonatype.password="$SONA_PASSWORD" \
-s $SETTINGS_FILE
-s $SETTINGS_FILE \
--no-transfer-progress


batch:
Expand Down
3 changes: 2 additions & 1 deletion codebuild/release/release-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ phases:
-Dgpg.passphrase="$GPG_PASS" \
-Dcodeartifact.token=$CODEARTIFACT_TOKEN \
-DaltDeploymentRepository=codeartifact::default::$CODEARTIFACT_REPO_URL \
-s $SETTINGS_FILE
-s $SETTINGS_FILE \
--no-transfer-progress

batch:
fast-fail: false
Expand Down
3 changes: 2 additions & 1 deletion codebuild/release/validate-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ phases:
-Dcheckstyle.skip \
-Dddbec.version=$VERSION \
-Dmaven.compiler.target=$JAVA_NUMERIC_VERSION \
-Dmaven.compiler.source=$JAVA_NUMERIC_VERSION
-Dmaven.compiler.source=$JAVA_NUMERIC_VERSION \
--no-transfer-progress
3 changes: 2 additions & 1 deletion codebuild/release/validate-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ phases:
-Dmaven.compiler.source=$JAVA_NUMERIC_VERSION \
-Dcodeartifact.token=$CODEARTIFACT_TOKEN \
-Dcodeartifact.url=$CODEARTIFACT_REPO_URL \
-s $SETTINGS_FILE
-s $SETTINGS_FILE \
--no-transfer-progress
2 changes: 1 addition & 1 deletion codebuild/static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ phases:
java: corretto11
build:
commands:
- mvn com.coveo:fmt-maven-plugin:check
- mvn com.coveo:fmt-maven-plugin:check --no-transfer-progress
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amazonaws.examples;

import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch;

/**
* This demonstrates how to use the {@link DynamoDBMapper} with the {@link AttributeEncryptor} to
* encrypt your data. Before you can use this you need to set up a DynamoDB table called
* "ExampleTable" to hold the encrypted data. "ExampleTable" should have a partition key named
* "partition_attribute" for Strings and a sort (range) key named "sort_attribute" for numbers.
*/
public class AwsKmsEncryptedGlobal2017Object {
public static final String EXAMPLE_TABLE_NAME = "ExampleTable";
public static final String PARTITION_ATTRIBUTE = "partition_attribute";
public static final String SORT_ATTRIBUTE = "sort_attribute";

private static final String AWS_DYNAMODB_REPLICATION_DELETING_ATTRIBUTE = "aws:rep:deleting";
private static final String AWS_DYNAMODB_REPLICATION_UPDATETIME_ATTRIBUTE = "aws:rep:updatetime";
private static final String AWS_DYNAMODB_REPLICATION_UPDATEREGION_ATTRIBUTE =
"aws:rep:updateregion";

private static final String STRING_FIELD_NAME = "example";

@DynamoDBTable(tableName = EXAMPLE_TABLE_NAME)
public static final class DataPoJo {
private String partitionAttribute;
private int sortAttribute;
private String example;
private boolean aws_dynamodb_replication_deleting;
private float aws_dynamodb_replication_updatetime;
private String aws_dynamodb_replication_updateregion;

@DynamoDBHashKey(attributeName = PARTITION_ATTRIBUTE)
public String getPartitionAttribute() {
return partitionAttribute;
}

public void setPartitionAttribute(String partitionAttribute) {
this.partitionAttribute = partitionAttribute;
}

@DynamoDBRangeKey(attributeName = SORT_ATTRIBUTE)
public int getSortAttribute() {
return sortAttribute;
}

public void setSortAttribute(int sortAttribute) {
this.sortAttribute = sortAttribute;
}

@DynamoDBAttribute(attributeName = STRING_FIELD_NAME)
public String getExample() {
return example;
}

public void setExample(String example) {
this.example = example;
}

@DynamoDBAttribute(attributeName = AWS_DYNAMODB_REPLICATION_DELETING_ATTRIBUTE)
@DynamoDBIgnore
@DoNotTouch
public boolean getAws_dynamodb_replication_deleting() {
return aws_dynamodb_replication_deleting;
}

public void setAws_dynamodb_replication_deleting(boolean deleting) {
this.aws_dynamodb_replication_deleting = deleting;
}

@DynamoDBAttribute(attributeName = AWS_DYNAMODB_REPLICATION_UPDATETIME_ATTRIBUTE)
@DynamoDBIgnore
@DoNotTouch
public float getAws_dynamodb_replication_updatetime() {
return aws_dynamodb_replication_updatetime;
}

public void setAws_dynamodb_replication_updatetime(float updatetime) {
this.aws_dynamodb_replication_updatetime = updatetime;
}

@DynamoDBAttribute(attributeName = AWS_DYNAMODB_REPLICATION_UPDATEREGION_ATTRIBUTE)
@DynamoDBIgnore
@DoNotTouch
public String getAws_dynamodb_replication_updateregion() {
return aws_dynamodb_replication_updateregion;
}

public void setAws_dynamodb_replication_updateregion(String updateregion) {
this.aws_dynamodb_replication_updateregion = updateregion;
}

@Override
public String toString() {
return "DataPoJo [partitionAttribute="
+ partitionAttribute
+ ", sortAttribute="
+ sortAttribute
+ ", example="
+ example
+ "]";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static void encryptRecord(
AWSKMS kmsEncrypt = null;
AmazonDynamoDB ddbEncrypt = null;
AmazonDynamoDB ddbDecrypt = null;
//noinspection DuplicatedCode
try {
// Sample object to be encrypted
AwsKmsEncryptedObject.DataPoJo record = new AwsKmsEncryptedObject.DataPoJo();
Expand All @@ -52,6 +53,7 @@ public static void encryptRecord(
// Set up clients and configuration in the first region. All of this is thread-safe and can be
// reused
// across calls
@SuppressWarnings("DuplicatedCode")
final String encryptRegion = cmkArnEncrypt.split(":")[3];
kmsEncrypt = AWSKMSClientBuilder.standard().withRegion(encryptRegion).build();
ddbEncrypt = AmazonDynamoDBClientBuilder.standard().withRegion(encryptRegion).build();
Expand Down Expand Up @@ -79,7 +81,7 @@ public static void encryptRecord(
// to the second region
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
} catch (InterruptedException ignored) {
}

// Set up clients and configuration in the second region
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazonaws.examples;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.TableNameOverride;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.DirectKmsMaterialProvider;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
import java.security.GeneralSecurityException;
import java.util.Random;

/**
* Example showing use of AWS KMS CMP with an AWS KMS Multi-Region Key. We encrypt a record with a
* key in one region, then decrypt the ciphertext with the same key replicated to another region.
*
* <p>This example assumes that you have a DDB Global Table replicated to two regions, and an AWS
* KMS Multi-Region Key replicated to the same regions.
*/
public class AwsKmsMultiRegionKeyGlobal2017 {

public static void main(String[] args) throws GeneralSecurityException {
final String tableName = args[0];
final String cmkArn1 = args[1];
final String cmkArn2 = args[2];

encryptAndDecrypt(tableName, cmkArn1, cmkArn2);
}

public static void encryptAndDecrypt(
final String tableName, final String cmkArnEncrypt, final String cmkArnDecrypt)
throws GeneralSecurityException {
AWSKMS kmsDecrypt = null;
AWSKMS kmsEncrypt = null;
AmazonDynamoDB ddbEncrypt = null;
AmazonDynamoDB ddbDecrypt = null;
//noinspection DuplicatedCode
try {
Random random = new Random();
int sort_value = random.nextInt();
// Sample object to be encrypted
AwsKmsEncryptedGlobal2017Object.DataPoJo record =
new AwsKmsEncryptedGlobal2017Object.DataPoJo();
record.setPartitionAttribute("is this");
record.setSortAttribute(sort_value);
record.setExample("data");

// Set up clients and configuration in the first region. All of this is thread-safe and can be
// reused
// across calls
final String encryptRegion = cmkArnEncrypt.split(":")[3];
kmsEncrypt = AWSKMSClientBuilder.standard().withRegion(encryptRegion).build();
ddbEncrypt = AmazonDynamoDBClientBuilder.standard().withRegion(encryptRegion).build();
final DirectKmsMaterialProvider cmpEncrypt =
new DirectKmsMaterialProvider(kmsEncrypt, cmkArnEncrypt);
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmpEncrypt);

// Mapper Creation
// Please note the use of SaveBehavior.PUT (SaveBehavior.CLOBBER works as well).
// Omitting this can result in data-corruption.
DynamoDBMapperConfig mapperConfig =
DynamoDBMapperConfig.builder()
.withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.PUT)
.withTableNameOverride(TableNameOverride.withTableNameReplacement(tableName))
.build();
DynamoDBMapper encryptMapper =
new DynamoDBMapper(ddbEncrypt, mapperConfig, new AttributeEncryptor(encryptor));

System.out.println("Global 2017 Plaintext Record: " + record);
// Save the item to the DynamoDB table
encryptMapper.save(record);

// DDB Global Table replication takes some time. Sleep for a moment to give the item a chance
// to replicate
// to the second region
try {
Thread.sleep(5000);
} catch (InterruptedException ignored) {
}

// Set up clients and configuration in the second region
final String decryptRegion = cmkArnDecrypt.split(":")[3];
kmsDecrypt = AWSKMSClientBuilder.standard().withRegion(decryptRegion).build();
ddbDecrypt = AmazonDynamoDBClientBuilder.standard().withRegion(decryptRegion).build();
final DirectKmsMaterialProvider cmpDecrypt =
new DirectKmsMaterialProvider(kmsDecrypt, cmkArnDecrypt);
final DynamoDBEncryptor decryptor = DynamoDBEncryptor.getInstance(cmpDecrypt);

DynamoDBMapper decryptMapper =
new DynamoDBMapper(ddbDecrypt, mapperConfig, new AttributeEncryptor(decryptor));

// Retrieve (and decrypt) it in the second region. This allows you to avoid a cross-region KMS
// call to the
// first region if your application is running in the second region
AwsKmsEncryptedGlobal2017Object.DataPoJo decryptedRecord =
decryptMapper.load(AwsKmsEncryptedGlobal2017Object.DataPoJo.class, "is this", sort_value);
System.out.println("Global 2017 Decrypted Record: " + decryptedRecord);

// The decrypted fields match the original fields before encryption
assert record.getExample().equals(decryptedRecord.getExample());

decryptedRecord.setExample("Howdy");
encryptMapper.save(decryptedRecord);
try {
Thread.sleep(5000);
} catch (InterruptedException ignored) {
}
AwsKmsEncryptedGlobal2017Object.DataPoJo decryptedRecordTwo =
decryptMapper.load(AwsKmsEncryptedGlobal2017Object.DataPoJo.class, "is this", sort_value);
System.out.println("Global 2017 Decrypted Record: " + decryptedRecord);
assert decryptedRecordTwo.getExample().equals(decryptedRecord.getExample());

encryptMapper.delete(decryptedRecordTwo);
} finally {
if (kmsDecrypt != null) {
kmsDecrypt.shutdown();
}
if (kmsEncrypt != null) {
kmsEncrypt.shutdown();
}
if (ddbEncrypt != null) {
ddbEncrypt.shutdown();
}
if (ddbDecrypt != null) {
ddbDecrypt.shutdown();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.examples;

import static com.amazonaws.examples.TestUtils.US_EAST_1_MRK_KEY_ID;
import static com.amazonaws.examples.TestUtils.US_WEST_2_MRK_KEY_ID;

import java.security.GeneralSecurityException;
import org.testng.annotations.Test;

public class AwsKmsMultiRegionKeyGlobal2017IT {
private static final String TABLE_NAME = "tonyknap-manual-mrk-global-2017";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more generic table name, since this goes to our examples? "I think TableName" or "SuperCoolTable" are worthy candidates?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we ever going to ship this... but if we did, yes.


@Test
public void testEncryptAndDecrypt() throws GeneralSecurityException {
AwsKmsMultiRegionKeyGlobal2017.encryptAndDecrypt(
TABLE_NAME, US_EAST_1_MRK_KEY_ID, US_WEST_2_MRK_KEY_ID);
}
}