From b2d2c66e8f82f0b48f359b719f20a34e83c50cda Mon Sep 17 00:00:00 2001 From: Robin Salkeld Date: Thu, 11 Feb 2021 15:06:03 -0800 Subject: [PATCH 001/105] Bump version --- CHANGELOG.md | 4 ++++ README.md | 2 +- examples/pom.xml | 6 +++--- pom.xml | 2 +- sdk1/pom.xml | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5102db35..4152f315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changelog +## 2.0.1 -- 2021-02-11 +### Bugfix +Fixes released jar files to ensure JDK 8 compatibility. + ## 2.0.0 -- 2021-02-04 Removes MostRecentProvider. MostRecentProvider is replaced by CachingMostRecentProvider as of 1.15.0. diff --git a/README.md b/README.md index e7816de1..af9f85ac 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ You can download the [latest snapshot release][download] or pick it up from Mave com.amazonaws aws-dynamodb-encryption-java - 2.0.0 + 2.0.1 ``` diff --git a/examples/pom.xml b/examples/pom.xml index b4c4a6be..ed2d7775 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -8,12 +8,12 @@ software.amazon.cryptools dynamodbencryptionclient-pom - 2.0.0 + 2.0.1 dynamodbencryptionclient-sdk1examples jar - 2.0.0 + 2.0.1 aws-dynamodb-encryption-java :: SDK1 Examples Examples for AWS DynamoDB Encryption Client for AWS Java SDK v1 @@ -27,7 +27,7 @@ com.amazonaws aws-dynamodb-encryption-java - 2.0.0 + 2.0.1 diff --git a/pom.xml b/pom.xml index 059ed20f..238301fb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 software.amazon.cryptools dynamodbencryptionclient-pom - 2.0.0 + 2.0.1 pom aws-dynamodb-encryption-java :: POM diff --git a/sdk1/pom.xml b/sdk1/pom.xml index b902d530..2c0aa5f9 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.amazonaws aws-dynamodb-encryption-java - 2.0.0 + 2.0.1 jar aws-dynamodb-encryption-java :: SDK1 AWS DynamoDB Encryption Client for AWS Java SDK v1 From 619c7bdce09b261009034cb13a2d1ecc67eb3d80 Mon Sep 17 00:00:00 2001 From: Robin Salkeld Date: Thu, 11 Feb 2021 16:25:51 -0800 Subject: [PATCH 002/105] Fix release date of 2.0.1 in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4152f315..148194f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # Changelog -## 2.0.1 -- 2021-02-11 +## 2.0.1 -- 2021-02-12 ### Bugfix Fixes released jar files to ensure JDK 8 compatibility. From fd6b20ed0912cf00ead90f5771e8e78d71cf02f5 Mon Sep 17 00:00:00 2001 From: lizroth <30636882+lizroth@users.noreply.github.com> Date: Fri, 5 Mar 2021 10:58:50 -0800 Subject: [PATCH 003/105] fix: Data model migration doc breadcrumb. Merge pull request #139 from lizroth/migration-doc-note --- .gitignore | 1 + README.md | 12 ++++++++++++ .../dynamodbv2/datamodeling/AttributeEncryptor.java | 10 +++++++--- .../datamodeling/encryption/DoNotEncrypt.java | 6 +++++- .../datamodeling/encryption/DoNotTouch.java | 4 ++++ .../datamodeling/encryption/DynamoDBEncryptor.java | 6 +++++- .../datamodeling/encryption/DynamoDBSigner.java | 4 ++++ .../encryption/HandleUnknownAttributes.java | 6 +++++- .../datamodeling/encryption/TableAadOverride.java | 4 ++++ .../datamodeling/TransformerHolisticIT.java | 2 +- 10 files changed, 48 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 97c18da1..c2525ee2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ release.sh target .idea/ *.iml +.DS_Store diff --git a/README.md b/README.md index af9f85ac..22d00cde 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,18 @@ Note that by default all attributes except the primary keys are both encrypted a There is a variety of existing [EncryptionMaterialsProvider][materialprovider] implementations that you can use to provide the encryption material, including [KeyStoreMaterialsProvider][keystoreprovider] which makes use of a Java keystore. Alternatively, you can also plug in your own custom implementation. +### Changing Your Data Model + +Every time you encrypt or decrypt an item, you need to provide attribute actions that tell the DynamoDB Encryption +Client which attributes to encrypt and sign, which attributes to sign (but not encrypt), and which to ignore. Attribute +actions are not saved in the encrypted item and the DynamoDB Encryption Client does not update your attribute actions +automatically. + +Whenever you change your data model, that is, when you add or remove attributes from your table items, you need to take +additional steps to safely migrate the client-side encryption configuration. + +For guidance on this process, please see the developer guide on [Changing Your Data Model](https://docs.aws.amazon.com/dynamodb-encryption-client/latest/devguide/data-model.html). + ### Downloads You can download the [latest snapshot release][download] or pick it up from Maven: diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java index 146e77d2..1727a140 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java @@ -38,9 +38,13 @@ /** * Encrypts all non-key fields prior to storing them in DynamoDB. - * This must be used with @{link SaveBehavior#PUT} or @{link SaveBehavior#CLOBBER}. - * - * @author Greg Rubin + * This must be used with {@link SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}. + * + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

+ * + * @author Greg Rubin */ public class AttributeEncryptor implements AttributeTransformer { private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class); diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java index 501bc642..fcf067a8 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java @@ -23,7 +23,11 @@ /** * Prevents the associated item (class or attribute) from being encrypted. - * + * + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

+ * * @author Greg Rubin */ @DynamoDB diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java index d2a817fd..ee2be7ec 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java @@ -23,6 +23,10 @@ /** * Prevents the associated item from being encrypted or signed. + * + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

* * @author Greg Rubin */ diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 7a70291c..678b0e40 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -50,7 +50,11 @@ /** * The low-level API used by {@link AttributeEncryptor} to perform crypto * operations on the record attributes. - * + * + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

+ * * @author Greg Rubin */ public class DynamoDBEncryptor { diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java index cdded8fd..f4a13905 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java @@ -43,6 +43,10 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; /** + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

+ * * @author Greg Rubin */ // NOTE: This class must remain thread-safe. diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java index b067ac55..fafa85b5 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java @@ -30,8 +30,12 @@ * attributes will only be included in the signature calculation, and if it's * added to a class with default encryption behavior, the unknown attributes * will be signed and decrypted. + * + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

* - * @author Dan Cavallaro + * @author Dan Cavallaro */ @Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java index 9bd58a0c..eb9c15db 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java @@ -24,6 +24,10 @@ * {@code tableName} instead. This can be useful when multiple tables are * used interchangably and data should be able to be copied or moved * between them without needing to be reencrypted. + * + *

For guidance on performing a safe data model change procedure, please see + * + * DynamoDB Encryption Client Developer Guide: Changing your data model

* * @author Greg Rubin */ diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java index e982121f..521d908a 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java @@ -397,7 +397,7 @@ public void simpleSaveLoad() { /** * This test ensures that optimistic locking can be successfully done through the {@link DynamoDBMapper} when - * combined with the @{link AttributeEncryptor}. Specifically it checks that {@link SaveBehavior#PUT} properly + * combined with the {@link AttributeEncryptor}. Specifically it checks that {@link SaveBehavior#PUT} properly * enforces versioning and will result in a {@link ConditionalCheckFailedException} when optimistic locking should * prevent a write. Finally, it checks that {@link SaveBehavior#CLOBBER} properly ignores optimistic locking and * overwrites the old value. From bf72702e171ff820220f437735d28f01fe92773c Mon Sep 17 00:00:00 2001 From: Ben Farley <47006790+farleyb-amazon@users.noreply.github.com> Date: Thu, 18 Mar 2021 11:49:41 -0600 Subject: [PATCH 004/105] fix: Update user agent string to correctly reflect version (#141) * fix: Update user agent string to correctly reflect version --- sdk1/pom.xml | 7 +++++++ .../providers/DirectKmsMaterialProvider.java | 7 +++---- .../dynamodbv2/datamodeling/internal/Utils.java | 16 ++++++++++++++++ sdk1/src/main/resources/project.properties | 1 + .../providers/DirectKmsMaterialProviderTest.java | 14 ++++++++++++++ 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 sdk1/src/main/resources/project.properties diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 2c0aa5f9..8620ff72 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -272,6 +272,13 @@ + + + src/main/resources + true + + + org.apache.maven.plugins diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java index bee842ba..a655fa86 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java @@ -30,7 +30,6 @@ import com.amazonaws.services.kms.model.GenerateDataKeyRequest; import com.amazonaws.services.kms.model.GenerateDataKeyResult; import com.amazonaws.util.StringUtils; -import com.amazonaws.util.VersionInfoUtils; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -43,6 +42,7 @@ import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.CONTENT_KEY_ALGORITHM; import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.ENVELOPE_KEY; import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.KEY_WRAPPING_ALGORITHM; +import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.loadVersion; /** * Generates a unique data key for each record in DynamoDB and protects that key @@ -53,9 +53,8 @@ * @see KMS Encryption Context */ public class DirectKmsMaterialProvider implements EncryptionMaterialsProvider { - private static final String VERSION_STRING = "1.0"; - private static final String USER_AGENT = DirectKmsMaterialProvider.class.getName() - + "/" + VERSION_STRING + "/" + VersionInfoUtils.getVersion(); + static final String USER_AGENT_PREFIX = "DynamodbEncryptionSdkJava/"; + private static final String USER_AGENT = USER_AGENT_PREFIX + loadVersion(); private static final String COVERED_ATTR_CTX_KEY = "aws-kms-ec-attr"; private static final String SIGNING_KEY_ALGORITHM = "amzn-ddb-sig-alg"; private static final String TABLE_NAME_EC_KEY = "*aws-kms-table*"; diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java index 5ee04895..86e6604a 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java @@ -14,7 +14,9 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; +import java.io.IOException; import java.security.SecureRandom; +import java.util.Properties; public class Utils { private static final ThreadLocal RND = new ThreadLocal() { @@ -47,4 +49,18 @@ public static V checkNotNull(final V ref, final String errMsg) { return ref; } } + + /* + * Loads the version of the library + */ + public static String loadVersion() { + try { + final Properties properties = new Properties(); + properties.load(ClassLoader.getSystemResourceAsStream("project.properties")); + return properties.getProperty("version"); + } catch (final IOException ex) { + return "unknown"; + } + } + } diff --git a/sdk1/src/main/resources/project.properties b/sdk1/src/main/resources/project.properties new file mode 100644 index 00000000..defbd482 --- /dev/null +++ b/sdk1/src/main/resources/project.properties @@ -0,0 +1 @@ +version=${project.version} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java index 8eb71f73..de4b60b5 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java @@ -12,6 +12,7 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import com.amazonaws.RequestClientOptions; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; @@ -337,6 +338,19 @@ public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) { assertTrue(gdkCalled.get()); } + @Test + public void userAgentIsAdded() { + AWSKMS kmsSpy = new FakeKMS() { + @Override + public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) { + assertTrue(r.getRequestClientOptions().getClientMarker(RequestClientOptions.Marker.USER_AGENT) + .contains(DirectKmsMaterialProvider.USER_AGENT_PREFIX)); + return super.generateDataKey(r); + } + }; + new DirectKmsMaterialProvider(kmsSpy, keyId).getEncryptionMaterials(ctx); + } + private static class ExtendedKmsMaterialProvider extends DirectKmsMaterialProvider { private final String encryptionKeyIdAttributeName; From 07fb66b447eb5a76b225f2368081ace6b3915334 Mon Sep 17 00:00:00 2001 From: Robin Salkeld Date: Tue, 25 May 2021 12:52:48 -0700 Subject: [PATCH 005/105] chore: Add repo-sync actions (#143) See https://github.com/repo-sync/repo-sync --- .github/workflows/repo-sync.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/repo-sync.yml diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml new file mode 100644 index 00000000..b7605354 --- /dev/null +++ b/.github/workflows/repo-sync.yml @@ -0,0 +1,25 @@ +name: Repo Sync + +on: + workflow_dispatch: # allows triggering this manually through the Actions UI + +jobs: + repo-sync: + name: Repo Sync + environment: repo-sync + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: repo-sync/github-sync@v2 + name: Sync repo to branch + with: + source_repo: ${{ secrets.SOURCE_REPO }} + source_branch: master + destination_branch: ${{ secrets.INTERMEDIATE_BRANCH }} + github_token: ${{ secrets.GITHUB_TOKEN }} + - uses: repo-sync/pull-request@v2 + name: Create pull request + with: + source_branch: ${{ secrets.INTERMEDIATE_BRANCH }} + destination_branch: master + github_token: ${{ secrets.GITHUB_TOKEN }} From bafd6fb7a3fe25752b5e9a50b85db68869055469 Mon Sep 17 00:00:00 2001 From: Alex Chew Date: Tue, 1 Jun 2021 14:47:45 -0700 Subject: [PATCH 006/105] chore: add issue template (#142) Co-authored-by: Robin Salkeld --- ...amazon-dynamodb-encryption-client-issue.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md diff --git a/.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md b/.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md new file mode 100644 index 00000000..d5b0433d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md @@ -0,0 +1,26 @@ +--- +name: Amazon DynamoDB Encryption Client Issue +about: Amazon DynamoDB Encryption Client Issue +title: '' +labels: '' +assignees: '' + +--- + +### Security issue notifications + +If you discover a potential security issue in the Amazon DynamoDB Encryption Client we ask that you notify AWS Security via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue. + +### Problem: + +A short description of what the problem is and why we need to fix it. Add reproduction steps if necessary. + +### Solution: + +A description of the possible solution in terms of DynamoDB Encryption Client architecture. + +### Out of scope: + +Is there anything the solution will intentionally NOT address? + +[//]: # (NOTE: If you believe this might be a security issue, please email aws-security@amazon.com instead of creating a GitHub issue. For more details, see the AWS Vulnerability Reporting Guide: https://aws.amazon.com/security/vulnerability-reporting/ ) From 731e880b3858a1dff21ff7746308cd8fb7a5fe1e Mon Sep 17 00:00:00 2001 From: Ben Farley <47006790+farleyb-amazon@users.noreply.github.com> Date: Thu, 17 Jun 2021 16:12:29 -0600 Subject: [PATCH 007/105] chore: Add examples for MRKs (#145) --- .../examples/AwsKmsMultiRegionKey.java | 111 ++++++++++++++++++ .../examples/AwsKmsMultiRegionKeyIT.java | 20 ++++ .../com/amazonaws/examples/TestUtils.java | 2 + 3 files changed, 133 insertions(+) create mode 100644 examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java create mode 100644 examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java new file mode 100644 index 00000000..5ba27db0 --- /dev/null +++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java @@ -0,0 +1,111 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.amazonaws.examples; + +import java.security.GeneralSecurityException; +import java.util.Arrays; + +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; + +/** + * 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. + * + * 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 AwsKmsMultiRegionKey { + + public static void main(String[] args) throws GeneralSecurityException { + final String tableName = args[0]; + final String cmkArn1 = args[1]; + final String cmkArn2 = args[2]; + + encryptRecord(tableName, cmkArn1, cmkArn2); + } + + public static void encryptRecord(final String tableName, final String cmkArnEncrypt, final String cmkArnDecrypt) throws GeneralSecurityException { + AWSKMS kmsDecrypt = null; + AWSKMS kmsEncrypt = null; + AmazonDynamoDB ddbEncrypt = null; + AmazonDynamoDB ddbDecrypt = null; + try { + // Sample object to be encrypted + AwsKmsEncryptedObject.DataPoJo record = new AwsKmsEncryptedObject.DataPoJo(); + record.setPartitionAttribute("is this"); + record.setSortAttribute(42); + record.setExample("data"); + record.setSomeNumbers(99); + record.setSomeBinary(new byte[]{0x00, 0x01, 0x02}); + record.setLeaveMe("alone"); + + // 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("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(1000); + } catch (InterruptedException e) {} + + // 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 + AwsKmsEncryptedObject.DataPoJo decryptedRecord = decryptMapper.load(AwsKmsEncryptedObject.DataPoJo.class, "is this", 42); + System.out.println("Decrypted Record: " + decryptedRecord); + + // The decrypted fields match the original fields before encryption + assert record.getExample().equals(decryptedRecord.getExample()); + assert record.getSomeNumbers() == decryptedRecord.getSomeNumbers(); + assert Arrays.equals(record.getSomeBinary(), decryptedRecord.getSomeBinary()); + } finally { + if (kmsDecrypt != null) { + kmsDecrypt.shutdown(); + } + if (kmsEncrypt != null) { + kmsEncrypt.shutdown(); + } + if (ddbEncrypt != null) { + ddbEncrypt.shutdown(); + } + if (ddbDecrypt != null) { + ddbDecrypt.shutdown(); + } + } + } +} diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java new file mode 100644 index 00000000..28ba4f34 --- /dev/null +++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java @@ -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 org.testng.annotations.Test; + +import java.security.GeneralSecurityException; + +import static com.amazonaws.examples.TestUtils.US_EAST_1_MRK_KEY_ID; +import static com.amazonaws.examples.TestUtils.US_WEST_2_MRK_KEY_ID; + +public class AwsKmsMultiRegionKeyIT { + private static final String TABLE_NAME = "ddbec-mrk-testing"; + + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + AwsKmsMultiRegionKey.encryptRecord(TABLE_NAME, US_EAST_1_MRK_KEY_ID, US_WEST_2_MRK_KEY_ID); + } +} diff --git a/examples/src/test/java/com/amazonaws/examples/TestUtils.java b/examples/src/test/java/com/amazonaws/examples/TestUtils.java index 15030b50..349e1a41 100644 --- a/examples/src/test/java/com/amazonaws/examples/TestUtils.java +++ b/examples/src/test/java/com/amazonaws/examples/TestUtils.java @@ -23,6 +23,8 @@ private TestUtils() { */ public static final String US_WEST_2_KEY_ID = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"; public static final String US_WEST_2 = "us-west-2"; + public static final String US_EAST_1_MRK_KEY_ID = "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; + public static final String US_WEST_2_MRK_KEY_ID = "arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; public static void createDDBTable(AmazonDynamoDB ddb, String tableName, String partitionName, String sortName) { ArrayList attrDef = new ArrayList(); From 873b7a725f4df7c0fea0a337fffea8ba82bb717f Mon Sep 17 00:00:00 2001 From: lavaleri <49660121+lavaleri@users.noreply.github.com> Date: Tue, 22 Jun 2021 10:34:01 -0700 Subject: [PATCH 008/105] chore: Add format check to CI and format (#146) --- buildspec.yml | 5 + codebuild/static-analysis.yml | 9 + .../examples/AsymmetricEncryptedItem.java | 84 +- .../examples/AwsKmsEncryptedItem.java | 83 +- .../examples/AwsKmsEncryptedObject.java | 71 +- .../examples/AwsKmsMultiRegionKey.java | 169 +- ...ionContextOverridesWithDynamoDBMapper.java | 301 +-- .../examples/MostRecentEncryptedItem.java | 102 +- .../examples/SymmetricEncryptedItem.java | 92 +- .../examples/AsymmetricEncryptedItemTest.java | 21 +- .../examples/AwsKmsEncryptedItemIT.java | 21 +- .../examples/AwsKmsEncryptedObjectIT.java | 28 +- .../examples/AwsKmsMultiRegionKeyIT.java | 17 +- ...nContextOverridesWithDynamoDBMapperIT.java | 35 +- .../examples/MostRecentEncryptedItemIT.java | 38 +- .../examples/SymmetricEncryptedItemTest.java | 31 +- .../com/amazonaws/examples/TestUtils.java | 82 +- pom.xml | 10 + .../datamodeling/AttributeEncryptor.java | 450 +++-- .../datamodeling/encryption/DelegatedKey.java | 203 +- .../datamodeling/encryption/DoNotEncrypt.java | 15 +- .../datamodeling/encryption/DoNotTouch.java | 17 +- .../encryption/DynamoDBEncryptor.java | 1067 +++++------ .../encryption/DynamoDBSigner.java | 387 ++-- .../encryption/EncryptionContext.java | 357 ++-- .../encryption/EncryptionFlags.java | 8 +- .../encryption/HandleUnknownAttributes.java | 27 +- .../encryption/TableAadOverride.java | 19 +- .../materials/AbstractRawMaterials.java | 77 +- .../materials/AsymmetricRawMaterials.java | 43 +- .../materials/CryptographicMaterials.java | 6 +- .../materials/DecryptionMaterials.java | 10 +- .../materials/EncryptionMaterials.java | 10 +- .../materials/SymmetricRawMaterials.java | 69 +- .../materials/WrappedRawMaterials.java | 325 ++-- .../providers/AsymmetricStaticProvider.java | 41 +- .../providers/CachingMostRecentProvider.java | 315 ++-- .../providers/DirectKmsMaterialProvider.java | 545 +++--- .../EncryptionMaterialsProvider.java | 78 +- .../providers/KeyStoreMaterialsProvider.java | 309 ++-- .../providers/SymmetricStaticProvider.java | 182 +- .../providers/WrappedMaterialsProvider.java | 244 ++- .../encryption/providers/store/MetaStore.java | 689 +++---- .../providers/store/ProviderStore.java | 107 +- .../utils/EncryptionContextOperators.java | 112 +- .../internal/AttributeValueMarshaller.java | 486 +++-- .../datamodeling/internal/Base64.java | 46 +- .../internal/ByteBufferInputStream.java | 60 +- .../datamodeling/internal/Hkdf.java | 475 +++-- .../datamodeling/internal/LRUCache.java | 113 +- .../datamodeling/internal/MsClock.java | 4 +- .../datamodeling/internal/TTLCache.java | 415 ++--- .../datamodeling/internal/Utils.java | 68 +- .../datamodeling/AttributeEncryptorTest.java | 1200 ++++++------ .../datamodeling/TransformerHolisticIT.java | 1569 ++++++++-------- .../encryption/DelegatedEncryptionTest.java | 476 ++--- .../DelegatedEnvelopeEncryptionTest.java | 476 ++--- .../encryption/DynamoDBEncryptorTest.java | 965 +++++----- .../encryption/DynamoDBSignerTest.java | 888 +++++---- .../materials/AsymmetricRawMaterialsTest.java | 213 +-- .../materials/SymmetricRawMaterialsTest.java | 137 +- .../AsymmetricStaticProviderTest.java | 190 +- .../CachingMostRecentProviderTests.java | 1126 ++++++------ .../DirectKmsMaterialProviderTest.java | 751 ++++---- .../KeyStoreMaterialsProviderTest.java | 529 +++--- .../SymmetricStaticProviderTest.java | 287 +-- .../WrappedMaterialsProviderTest.java | 745 ++++---- .../providers/store/MetaStoreTests.java | 609 +++--- .../utils/EncryptionContextOperatorsTest.java | 281 +-- .../AttributeValueMarshallerTest.java | 689 +++---- .../datamodeling/internal/Base64Tests.java | 145 +- .../internal/ByteBufferInputStreamTest.java | 107 +- .../internal/ConcurrentTTLCacheTest.java | 397 ++-- .../datamodeling/internal/HkdfTests.java | 336 ++-- .../datamodeling/internal/LRUCacheTest.java | 183 +- .../datamodeling/internal/TTLCacheTest.java | 697 +++---- .../BinaryAttributeByteArrayTestClass.java | 115 +- .../BinaryAttributeByteBufferTestClass.java | 115 +- .../CrossSDKVerificationTestClass.java | 755 ++++---- .../encryption/IndexRangeKeyTestClass.java | 303 ++- .../MapperQueryExpressionCryptoTest.java | 1005 +++++----- .../encryption/NoSuchTableTestClass.java | 17 +- .../encryption/NumberAttributeTestClass.java | 584 +++--- .../NumberSetAttributeTestClass.java | 404 ++-- .../mapper/encryption/RangeKeyTestClass.java | 277 ++- .../encryption/StringAttributeTestClass.java | 114 +- .../StringSetAttributeTestClass.java | 116 +- .../encryption/TestDynamoDBMapperFactory.java | 23 +- .../TestEncryptionMaterialsProvider.java | 48 +- .../integration/AutoGeneratedKeysITCase.java | 1632 ++++++++--------- .../mapper/integration/BatchWriteITCase.java | 826 +++++---- .../integration/BinaryAttributesITCase.java | 476 ++--- .../mapper/integration/ComplexTypeITCase.java | 566 +++--- .../integration/ConfigurationITCase.java | 389 ++-- .../DynamoDBCryptoIntegrationTestBase.java | 343 ++-- ...namoDBMapperCryptoIntegrationTestBase.java | 40 +- .../mapper/integration/DynamoDBTestBase.java | 107 +- .../integration/ExceptionHandlingITCase.java | 807 ++++---- .../HashKeyOnlyTableWithGSIITCase.java | 217 ++- .../IndexRangeKeyAttributesITCase.java | 719 ++++---- .../mapper/integration/InheritanceITCase.java | 482 +++-- .../mapper/integration/KeyOnlyPutITCase.java | 103 +- .../MapperLoadingStrategyConfigITCase.java | 566 +++--- ...erSaveConfigCryptoIntegrationTestBase.java | 315 ++-- .../integration/MapperSaveConfigITCase.java | 998 +++++----- .../NumericSetAttributesITCase.java | 292 +-- .../mapper/integration/QueryITCase.java | 320 ++-- .../integration/RangeKeyAttributesITCase.java | 282 +-- .../mapper/integration/ScanITCase.java | 425 ++--- .../SimpleNumericAttributesITCase.java | 485 ++--- .../SimpleStringAttributesITCase.java | 335 ++-- .../StringSetAttributesITCase.java | 242 +-- .../VersionAttributeUpdateITCase.java | 1134 ++++++------ .../dynamodbv2/testing/AttrMatcher.java | 171 +- .../testing/AttributeValueDeserializer.java | 77 +- .../testing/AttributeValueMatcher.java | 154 +- .../testing/AttributeValueSerializer.java | 66 +- .../dynamodbv2/testing/DdbRecordMatcher.java | 70 +- .../services/dynamodbv2/testing/FakeKMS.java | 314 ++-- .../dynamodbv2/testing/FakeParameters.java | 187 +- .../dynamodbv2/testing/ScenarioManifest.java | 124 +- .../dynamodbv2/testing/TestDelegatedKey.java | 175 +- .../dynamodbv2/testing/types/BaseClass.java | 319 ++-- .../types/BaseClassWithNewAttribute.java | 15 +- ...seClassWithUnknownAttributeAnnotation.java | 3 +- .../testing/types/DoNotEncryptField.java | 45 +- .../testing/types/DoNotTouchField.java | 45 +- .../dynamodbv2/testing/types/HashKeyOnly.java | 79 +- .../dynamodbv2/testing/types/KeysOnly.java | 84 +- .../dynamodbv2/testing/types/Mixed.java | 41 +- .../dynamodbv2/testing/types/SignOnly.java | 3 +- ...ignOnlyWithUnknownAttributeAnnotation.java | 3 +- ...wnAttributeAnnotationWithNewAttribute.java | 18 +- .../testing/types/TableOverride.java | 3 +- .../dynamodbv2/testing/types/Untouched.java | 3 +- .../types/UntouchedWithNewAttribute.java | 15 +- ...touchedWithUnknownAttributeAnnotation.java | 3 +- ...wnAttributeAnnotationWithNewAttribute.java | 18 +- 138 files changed, 20049 insertions(+), 19537 deletions(-) create mode 100644 codebuild/static-analysis.yml diff --git a/buildspec.yml b/buildspec.yml index 365eb003..1fc7d652 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -23,3 +23,8 @@ batch: env: env: image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 + - identifier: static_analysis + buildspec: codebuild/static-analysis.yml + env: + env: + image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 diff --git a/codebuild/static-analysis.yml b/codebuild/static-analysis.yml new file mode 100644 index 00000000..3c3b2f38 --- /dev/null +++ b/codebuild/static-analysis.yml @@ -0,0 +1,9 @@ +version: 0.2 + +phases: + install: + runtime-versions: + java: corretto11 + build: + commands: + - mvn com.coveo:fmt-maven-plugin:check diff --git a/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java index 2bf245f9..a5fb4ef9 100644 --- a/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java +++ b/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java @@ -14,6 +14,11 @@ */ package com.amazonaws.examples; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.KeyPair; @@ -23,15 +28,9 @@ import java.util.Map; import java.util.Set; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; - /** - * Example showing use of RSA keys for encryption and signing. - * For ease of the example, we create new random ones every time. + * Example showing use of RSA keys for encryption and signing. For ease of the example, we create + * new random ones every time. */ public class AsymmetricEncryptedItem { private static final String STRING_FIELD_NAME = "example"; @@ -50,7 +49,8 @@ public static void main(String[] args) throws GeneralSecurityException { encryptRecord(tableName, wrappingKeys, signingKeys); } - public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair signingKeys) throws GeneralSecurityException { + public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair signingKeys) + throws GeneralSecurityException { // Sample record to be encrypted final String partitionKeyName = "partition_attribute"; final String sortKeyName = "sort_attribute"; @@ -59,25 +59,34 @@ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair record.put(sortKeyName, new AttributeValue().withN("55")); record.put(STRING_FIELD_NAME, new AttributeValue().withS("data")); record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99")); - record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02}))); - record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute + record.put( + BINARY_FIELD_NAME, + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02}))); + record.put( + IGNORED_FIELD_NAME, + new AttributeValue().withS("alone")); // We want to ignore this attribute - // Set up our configuration and clients. All of this is thread-safe and can be reused across calls. + // Set up our configuration and clients. All of this is thread-safe and can be reused across + // calls. // Provider Configuration - final WrappedMaterialsProvider cmp = new WrappedMaterialsProvider(wrappingKeys.getPublic(), wrappingKeys.getPrivate(), signingKeys); + final WrappedMaterialsProvider cmp = + new WrappedMaterialsProvider( + wrappingKeys.getPublic(), wrappingKeys.getPrivate(), signingKeys); // Encryptor creation final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); // Information about the context of our data (normally just Table information) - final EncryptionContext encryptionContext = new EncryptionContext.Builder() - .withTableName(tableName) - .withHashKeyName(partitionKeyName) - .withRangeKeyName(sortKeyName) - .build(); + final EncryptionContext encryptionContext = + new EncryptionContext.Builder() + .withTableName(tableName) + .withHashKeyName(partitionKeyName) + .withRangeKeyName(sortKeyName) + .build(); // Describe what actions need to be taken for each attribute final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); - final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); + final EnumSet encryptAndSign = + EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); final Map> actions = new HashMap<>(); for (final String attributeName : record.keySet()) { switch (attributeName) { @@ -98,13 +107,22 @@ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair // End set-up // Encrypt the plaintext record directly - final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext); + final Map encrypted_record = + encryptor.encryptRecord(record, actions, encryptionContext); // Encrypted record fields change as expected - assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes - assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes - assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated - assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is + assert encrypted_record.get(STRING_FIELD_NAME).getB() + != null; // the encrypted string is stored as bytes + assert encrypted_record.get(NUMBER_FIELD_NAME).getB() + != null; // the encrypted number is stored as bytes + assert !record + .get(BINARY_FIELD_NAME) + .getB() + .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated + assert record + .get(IGNORED_FIELD_NAME) + .getS() + .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is // We could now put the encrypted item to DynamoDB just as we would any other item. // We're skipping it to to keep the example simpler. @@ -113,12 +131,22 @@ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair System.out.println("Encrypted Record: " + encrypted_record); // Decryption is identical. We'll pretend that we retrieved the record from DynamoDB. - final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext); + final Map decrypted_record = + encryptor.decryptRecord(encrypted_record, actions, encryptionContext); System.out.println("Decrypted Record: " + decrypted_record); // The decrypted fields match the original fields before encryption - assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS()); - assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); - assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); + assert record + .get(STRING_FIELD_NAME) + .getS() + .equals(decrypted_record.get(STRING_FIELD_NAME).getS()); + assert record + .get(NUMBER_FIELD_NAME) + .getN() + .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); + assert record + .get(BINARY_FIELD_NAME) + .getB() + .equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); } } diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java index 7c9f8431..f0d8896b 100644 --- a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java +++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java @@ -14,13 +14,6 @@ */ package com.amazonaws.examples; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags; @@ -28,10 +21,14 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; -/** - * Example showing use of AWS KMS CMP with record encryption functions directly. - */ +/** Example showing use of AWS KMS CMP with record encryption functions directly. */ public class AwsKmsEncryptedItem { private static final String STRING_FIELD_NAME = "example"; private static final String BINARY_FIELD_NAME = "and some binary"; @@ -54,7 +51,9 @@ public static void main(String[] args) throws GeneralSecurityException { } } - public static void encryptRecord(final String tableName, final String cmkArn, final AWSKMS kmsClient) throws GeneralSecurityException { + public static void encryptRecord( + final String tableName, final String cmkArn, final AWSKMS kmsClient) + throws GeneralSecurityException { // Sample record to be encrypted final String partitionKeyName = "partition_attribute"; final String sortKeyName = "sort_attribute"; @@ -63,10 +62,15 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi record.put(sortKeyName, new AttributeValue().withN("55")); record.put(STRING_FIELD_NAME, new AttributeValue().withS("data")); record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99")); - record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02}))); - record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute + record.put( + BINARY_FIELD_NAME, + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02}))); + record.put( + IGNORED_FIELD_NAME, + new AttributeValue().withS("alone")); // We want to ignore this attribute - // Set up our configuration and clients. All of this is thread-safe and can be reused across calls. + // Set up our configuration and clients. All of this is thread-safe and can be reused across + // calls. // This example assumes we already have a AWS KMS client `kmsClient` // Provider Configuration final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn); @@ -74,15 +78,17 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); // Information about the context of our data (normally just Table information) - final EncryptionContext encryptionContext = new EncryptionContext.Builder() - .withTableName(tableName) - .withHashKeyName(partitionKeyName) - .withRangeKeyName(sortKeyName) - .build(); + final EncryptionContext encryptionContext = + new EncryptionContext.Builder() + .withTableName(tableName) + .withHashKeyName(partitionKeyName) + .withRangeKeyName(sortKeyName) + .build(); // Describe what actions need to be taken for each attribute final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); - final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); + final EnumSet encryptAndSign = + EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); final Map> actions = new HashMap<>(); for (final String attributeName : record.keySet()) { switch (attributeName) { @@ -103,13 +109,22 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi // End set-up // Encrypt the plaintext record directly - final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext); + final Map encrypted_record = + encryptor.encryptRecord(record, actions, encryptionContext); // Encrypted record fields change as expected - assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes - assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes - assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated - assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is + assert encrypted_record.get(STRING_FIELD_NAME).getB() + != null; // the encrypted string is stored as bytes + assert encrypted_record.get(NUMBER_FIELD_NAME).getB() + != null; // the encrypted number is stored as bytes + assert !record + .get(BINARY_FIELD_NAME) + .getB() + .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated + assert record + .get(IGNORED_FIELD_NAME) + .getS() + .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is // We could now put the encrypted item to DynamoDB just as we would any other item. // We're skipping it to to keep the example simpler. @@ -118,12 +133,22 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi System.out.println("Encrypted Record: " + encrypted_record); // Decryption is identical. We'll pretend that we retrieved the record from DynamoDB. - final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext); + final Map decrypted_record = + encryptor.decryptRecord(encrypted_record, actions, encryptionContext); System.out.println("Decrypted Record: " + decrypted_record); // The decrypted fields match the original fields before encryption - assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS()); - assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); - assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); + assert record + .get(STRING_FIELD_NAME) + .getS() + .equals(decrypted_record.get(STRING_FIELD_NAME).getS()); + assert record + .get(NUMBER_FIELD_NAME) + .getN() + .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); + assert record + .get(BINARY_FIELD_NAME) + .getB() + .equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); } } diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java index b148fed5..55139bb4 100644 --- a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java +++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java @@ -14,12 +14,6 @@ */ package com.amazonaws.examples; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor; @@ -36,13 +30,17 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; /** - * 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. + * 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 AwsKmsEncryptedObject { public static final String EXAMPLE_TABLE_NAME = "ExampleTable"; @@ -74,43 +72,54 @@ public static void main(String[] args) throws GeneralSecurityException { } } - public static void encryptRecord(final String cmkArn, final AmazonDynamoDB ddbClient, final AWSKMS kmsClient) { + public static void encryptRecord( + final String cmkArn, final AmazonDynamoDB ddbClient, final AWSKMS kmsClient) { // Sample object to be encrypted DataPoJo record = new DataPoJo(); record.setPartitionAttribute("is this"); record.setSortAttribute(55); record.setExample("data"); record.setSomeNumbers(99); - record.setSomeBinary(new byte[]{0x00, 0x01, 0x02}); + record.setSomeBinary(new byte[] {0x00, 0x01, 0x02}); record.setLeaveMe("alone"); // Set up our configuration and clients - // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client `kmsClient` + // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client + // `kmsClient` final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn); // Encryptor creation final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); // 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(SaveBehavior.PUT).build(); - DynamoDBMapper mapper = new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor)); + DynamoDBMapperConfig mapperConfig = + DynamoDBMapperConfig.builder().withSaveBehavior(SaveBehavior.PUT).build(); + DynamoDBMapper mapper = + new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor)); System.out.println("Plaintext Record: " + record); // Save the item to the DynamoDB table mapper.save(record); - // Retrieve the encrypted item (directly without decrypting) from Dynamo so we can see it in our example + // Retrieve the encrypted item (directly without decrypting) from Dynamo so we can see it in our + // example final Map itemKey = new HashMap<>(); itemKey.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this")); itemKey.put(SORT_ATTRIBUTE, new AttributeValue().withN("55")); - final Map encrypted_record = ddbClient.getItem(EXAMPLE_TABLE_NAME, itemKey).getItem(); + final Map encrypted_record = + ddbClient.getItem(EXAMPLE_TABLE_NAME, itemKey).getItem(); System.out.println("Encrypted Record: " + encrypted_record); // Encrypted record fields change as expected - assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes - assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes - assert !ByteBuffer.wrap(record.getSomeBinary()).equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated - assert record.getLeaveMe().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is + assert encrypted_record.get(STRING_FIELD_NAME).getB() + != null; // the encrypted string is stored as bytes + assert encrypted_record.get(NUMBER_FIELD_NAME).getB() + != null; // the encrypted number is stored as bytes + assert !ByteBuffer.wrap(record.getSomeBinary()) + .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated + assert record + .getLeaveMe() + .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is // Retrieve (and decrypt) it from DynamoDB DataPoJo decrypted_record = mapper.load(DataPoJo.class, "is this", 55); @@ -188,11 +197,19 @@ public void setLeaveMe(String leaveMe) { @Override public String toString() { - return "DataPoJo [partitionAttribute=" + partitionAttribute + ", sortAttribute=" - + sortAttribute + ", example=" + example + ", someNumbers=" + someNumbers - + ", someBinary=" + Arrays.toString(someBinary) + ", leaveMe=" + leaveMe + "]"; + return "DataPoJo [partitionAttribute=" + + partitionAttribute + + ", sortAttribute=" + + sortAttribute + + ", example=" + + example + + ", someNumbers=" + + someNumbers + + ", someBinary=" + + Arrays.toString(someBinary) + + ", leaveMe=" + + leaveMe + + "]"; } - - } } diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java index 5ba27db0..b9173a69 100644 --- a/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java +++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java @@ -2,9 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.examples; -import java.security.GeneralSecurityException; -import java.util.Arrays; - import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor; @@ -15,97 +12,111 @@ 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.Arrays; /** - * 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. + * 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. * - * 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. + *

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 AwsKmsMultiRegionKey { - public static void main(String[] args) throws GeneralSecurityException { - final String tableName = args[0]; - final String cmkArn1 = args[1]; - final String cmkArn2 = args[2]; + public static void main(String[] args) throws GeneralSecurityException { + final String tableName = args[0]; + final String cmkArn1 = args[1]; + final String cmkArn2 = args[2]; - encryptRecord(tableName, cmkArn1, cmkArn2); - } + encryptRecord(tableName, cmkArn1, cmkArn2); + } - public static void encryptRecord(final String tableName, final String cmkArnEncrypt, final String cmkArnDecrypt) throws GeneralSecurityException { - AWSKMS kmsDecrypt = null; - AWSKMS kmsEncrypt = null; - AmazonDynamoDB ddbEncrypt = null; - AmazonDynamoDB ddbDecrypt = null; - try { - // Sample object to be encrypted - AwsKmsEncryptedObject.DataPoJo record = new AwsKmsEncryptedObject.DataPoJo(); - record.setPartitionAttribute("is this"); - record.setSortAttribute(42); - record.setExample("data"); - record.setSomeNumbers(99); - record.setSomeBinary(new byte[]{0x00, 0x01, 0x02}); - record.setLeaveMe("alone"); + public static void encryptRecord( + final String tableName, final String cmkArnEncrypt, final String cmkArnDecrypt) + throws GeneralSecurityException { + AWSKMS kmsDecrypt = null; + AWSKMS kmsEncrypt = null; + AmazonDynamoDB ddbEncrypt = null; + AmazonDynamoDB ddbDecrypt = null; + try { + // Sample object to be encrypted + AwsKmsEncryptedObject.DataPoJo record = new AwsKmsEncryptedObject.DataPoJo(); + record.setPartitionAttribute("is this"); + record.setSortAttribute(42); + record.setExample("data"); + record.setSomeNumbers(99); + record.setSomeBinary(new byte[] {0x00, 0x01, 0x02}); + record.setLeaveMe("alone"); - // 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); + // 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)); + // 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("Plaintext Record: " + record); - // Save the item to the DynamoDB table - encryptMapper.save(record); + System.out.println("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(1000); - } catch (InterruptedException e) {} + // 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(1000); + } catch (InterruptedException e) { + } - // 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); + // 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)); + 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 - AwsKmsEncryptedObject.DataPoJo decryptedRecord = decryptMapper.load(AwsKmsEncryptedObject.DataPoJo.class, "is this", 42); - System.out.println("Decrypted Record: " + decryptedRecord); + // 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 + AwsKmsEncryptedObject.DataPoJo decryptedRecord = + decryptMapper.load(AwsKmsEncryptedObject.DataPoJo.class, "is this", 42); + System.out.println("Decrypted Record: " + decryptedRecord); - // The decrypted fields match the original fields before encryption - assert record.getExample().equals(decryptedRecord.getExample()); - assert record.getSomeNumbers() == decryptedRecord.getSomeNumbers(); - assert Arrays.equals(record.getSomeBinary(), decryptedRecord.getSomeBinary()); - } finally { - if (kmsDecrypt != null) { - kmsDecrypt.shutdown(); - } - if (kmsEncrypt != null) { - kmsEncrypt.shutdown(); - } - if (ddbEncrypt != null) { - ddbEncrypt.shutdown(); - } - if (ddbDecrypt != null) { - ddbDecrypt.shutdown(); - } - } + // The decrypted fields match the original fields before encryption + assert record.getExample().equals(decryptedRecord.getExample()); + assert record.getSomeNumbers() == decryptedRecord.getSomeNumbers(); + assert Arrays.equals(record.getSomeBinary(), decryptedRecord.getSomeBinary()); + } finally { + if (kmsDecrypt != null) { + kmsDecrypt.shutdown(); + } + if (kmsEncrypt != null) { + kmsEncrypt.shutdown(); + } + if (ddbEncrypt != null) { + ddbEncrypt.shutdown(); + } + if (ddbDecrypt != null) { + ddbDecrypt.shutdown(); + } } + } } diff --git a/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java b/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java index 3ea7c218..07e4fcfa 100644 --- a/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java +++ b/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java @@ -14,6 +14,8 @@ */ package com.amazonaws.examples; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor; @@ -30,163 +32,174 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; - import java.security.GeneralSecurityException; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Set; -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap; - /** - * This demonstrates how to use an operator to override the table name used in the encryption context. - * Before you can use this you need to set up a DynamoDB table called "ExampleTableForEncryptionContextOverrides" - * to hold the encrypted data. - * "ExampleTableForEncryptionContextOverrides" should have a partition key named "partition_attribute" for Strings - * and a sort (range) key named "sort_attribute" for numbers. + * This demonstrates how to use an operator to override the table name used in the encryption + * context. Before you can use this you need to set up a DynamoDB table called + * "ExampleTableForEncryptionContextOverrides" to hold the encrypted data. + * "ExampleTableForEncryptionContextOverrides" should have a partition key named + * "partition_attribute" for Strings and a sort (range) key named "sort_attribute" for numbers. */ public class EncryptionContextOverridesWithDynamoDBMapper { - public static final String TABLE_NAME_TO_OVERRIDE = "ExampleTableForEncryptionContextOverrides"; - public static final String PARTITION_ATTRIBUTE = "partition_attribute"; - public static final String SORT_ATTRIBUTE = "sort_attribute"; - - private static final String STRING_FIELD_NAME = "example"; - private static final String BINARY_FIELD_NAME = "and some binary"; - private static final String NUMBER_FIELD_NAME = "some numbers"; - private static final String IGNORED_FIELD_NAME = "leave me"; - - public static void main(String[] args) throws GeneralSecurityException { - final String cmkArn = args[0]; - final String region = args[1]; - final String encryptionContextTableName = args[2]; - - AmazonDynamoDB ddb = null; - AWSKMS kms = null; - try { - ddb = AmazonDynamoDBClientBuilder.standard().withRegion(region).build(); - kms = AWSKMSClientBuilder.standard().withRegion(region).build(); - encryptRecord(cmkArn, encryptionContextTableName, ddb, kms); - } finally { - if (ddb != null) { - ddb.shutdown(); - } - if (kms != null) { - kms.shutdown(); - } - } + public static final String TABLE_NAME_TO_OVERRIDE = "ExampleTableForEncryptionContextOverrides"; + public static final String PARTITION_ATTRIBUTE = "partition_attribute"; + public static final String SORT_ATTRIBUTE = "sort_attribute"; + + private static final String STRING_FIELD_NAME = "example"; + private static final String BINARY_FIELD_NAME = "and some binary"; + private static final String NUMBER_FIELD_NAME = "some numbers"; + private static final String IGNORED_FIELD_NAME = "leave me"; + + public static void main(String[] args) throws GeneralSecurityException { + final String cmkArn = args[0]; + final String region = args[1]; + final String encryptionContextTableName = args[2]; + + AmazonDynamoDB ddb = null; + AWSKMS kms = null; + try { + ddb = AmazonDynamoDBClientBuilder.standard().withRegion(region).build(); + kms = AWSKMSClientBuilder.standard().withRegion(region).build(); + encryptRecord(cmkArn, encryptionContextTableName, ddb, kms); + } finally { + if (ddb != null) { + ddb.shutdown(); + } + if (kms != null) { + kms.shutdown(); + } + } + } + + public static void encryptRecord( + final String cmkArn, + final String newEncryptionContextTableName, + AmazonDynamoDB ddbClient, + AWSKMS kmsClient) + throws GeneralSecurityException { + // Sample object to be encrypted + ExampleItem record = new ExampleItem(); + record.setPartitionAttribute("is this"); + record.setSortAttribute(55); + record.setExample("my data"); + + // Set up our configuration and clients + // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client + // `kmsClient` + final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn); + final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); + + Map tableNameEncryptionContextOverrides = new HashMap<>(); + tableNameEncryptionContextOverrides.put(TABLE_NAME_TO_OVERRIDE, newEncryptionContextTableName); + tableNameEncryptionContextOverrides.put( + "AnotherExampleTableForEncryptionContextOverrides", "this table doesn't exist"); + + // Supply an operator to override the table name used in the encryption context + encryptor.setEncryptionContextOverrideOperator( + overrideEncryptionContextTableNameUsingMap(tableNameEncryptionContextOverrides)); + + // 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) + .build(); + DynamoDBMapper mapper = + new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor)); + + System.out.println("Plaintext Record: " + record.toString()); + // Save the record to the DynamoDB table + mapper.save(record); + + // Retrieve (and decrypt) it from DynamoDB + ExampleItem decrypted_record = mapper.load(ExampleItem.class, "is this", 55); + System.out.println("Decrypted Record: " + decrypted_record.toString()); + + // The decrypted field matches the original field before encryption + assert record.getExample().equals(decrypted_record.getExample()); + + // Setup new configuration to decrypt without using an overridden EncryptionContext + final Map itemKey = new HashMap<>(); + itemKey.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this")); + itemKey.put(SORT_ATTRIBUTE, new AttributeValue().withN("55")); + + final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); + final EnumSet encryptAndSign = + EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); + final Map encryptedItem = + ddbClient.getItem(TABLE_NAME_TO_OVERRIDE, itemKey).getItem(); + System.out.println("Encrypted Record: " + encryptedItem); + + Map> encryptionFlags = new HashMap<>(); + encryptionFlags.put(PARTITION_ATTRIBUTE, signOnly); + encryptionFlags.put(SORT_ATTRIBUTE, signOnly); + encryptionFlags.put(STRING_FIELD_NAME, encryptAndSign); + + final DynamoDBEncryptor encryptorWithoutOverrides = DynamoDBEncryptor.getInstance(cmp); + + // Decrypt the record without using an overridden EncryptionContext + Map decrypted_without_override_record = + encryptorWithoutOverrides.decryptRecord( + encryptedItem, + encryptionFlags, + new EncryptionContext.Builder() + .withHashKeyName(PARTITION_ATTRIBUTE) + .withRangeKeyName(SORT_ATTRIBUTE) + .withTableName(newEncryptionContextTableName) + .build()); + System.out.printf( + "The example item was encrypted using the table name '%s' in the EncryptionContext%n", + newEncryptionContextTableName); + + // The decrypted field matches the original field before encryption + assert record + .getExample() + .equals(decrypted_without_override_record.get(STRING_FIELD_NAME).getS()); + } + + @DynamoDBTable(tableName = TABLE_NAME_TO_OVERRIDE) + public static final class ExampleItem { + private String partitionAttribute; + private int sortAttribute; + private String example; + + @DynamoDBHashKey(attributeName = PARTITION_ATTRIBUTE) + public String getPartitionAttribute() { + return partitionAttribute; } - public static void encryptRecord(final String cmkArn, - final String newEncryptionContextTableName, - AmazonDynamoDB ddbClient, - AWSKMS kmsClient) throws GeneralSecurityException { - // Sample object to be encrypted - ExampleItem record = new ExampleItem(); - record.setPartitionAttribute("is this"); - record.setSortAttribute(55); - record.setExample("my data"); - - // Set up our configuration and clients - // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client `kmsClient` - final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn); - final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); - - Map tableNameEncryptionContextOverrides = new HashMap<>(); - tableNameEncryptionContextOverrides.put(TABLE_NAME_TO_OVERRIDE, newEncryptionContextTableName); - tableNameEncryptionContextOverrides.put("AnotherExampleTableForEncryptionContextOverrides", "this table doesn't exist"); - - // Supply an operator to override the table name used in the encryption context - encryptor.setEncryptionContextOverrideOperator( - overrideEncryptionContextTableNameUsingMap(tableNameEncryptionContextOverrides) - ); - - // 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).build(); - DynamoDBMapper mapper = new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor)); - - System.out.println("Plaintext Record: " + record.toString()); - // Save the record to the DynamoDB table - mapper.save(record); - - // Retrieve (and decrypt) it from DynamoDB - ExampleItem decrypted_record = mapper.load(ExampleItem.class, "is this", 55); - System.out.println("Decrypted Record: " + decrypted_record.toString()); - - // The decrypted field matches the original field before encryption - assert record.getExample().equals(decrypted_record.getExample()); - - // Setup new configuration to decrypt without using an overridden EncryptionContext - final Map itemKey = new HashMap<>(); - itemKey.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this")); - itemKey.put(SORT_ATTRIBUTE, new AttributeValue().withN("55")); - - final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); - final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); - final Map encryptedItem = ddbClient.getItem(TABLE_NAME_TO_OVERRIDE, itemKey) - .getItem(); - System.out.println("Encrypted Record: " + encryptedItem); - - Map> encryptionFlags = new HashMap<>(); - encryptionFlags.put(PARTITION_ATTRIBUTE, signOnly); - encryptionFlags.put(SORT_ATTRIBUTE, signOnly); - encryptionFlags.put(STRING_FIELD_NAME, encryptAndSign); - - final DynamoDBEncryptor encryptorWithoutOverrides = DynamoDBEncryptor.getInstance(cmp); - - // Decrypt the record without using an overridden EncryptionContext - Map decrypted_without_override_record = encryptorWithoutOverrides.decryptRecord(encryptedItem, - encryptionFlags, - new EncryptionContext.Builder().withHashKeyName(PARTITION_ATTRIBUTE) - .withRangeKeyName(SORT_ATTRIBUTE) - .withTableName(newEncryptionContextTableName) - .build()); - System.out.printf("The example item was encrypted using the table name '%s' in the EncryptionContext%n", newEncryptionContextTableName); - - // The decrypted field matches the original field before encryption - assert record.getExample().equals(decrypted_without_override_record.get(STRING_FIELD_NAME).getS()); + public void setPartitionAttribute(String partitionAttribute) { + this.partitionAttribute = partitionAttribute; } - @DynamoDBTable(tableName = TABLE_NAME_TO_OVERRIDE) - public static final class ExampleItem { - private String partitionAttribute; - private int sortAttribute; - private String example; - - @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; - } - - public String toString() { - return String.format("{partition_attribute: %s, sort_attribute: %s, example: %s}", - partitionAttribute, sortAttribute, example); - } + @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; + } + + public String toString() { + return String.format( + "{partition_attribute: %s, sort_attribute: %s, example: %s}", + partitionAttribute, sortAttribute, example); + } + } } diff --git a/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java index 5f6e870e..55f8a22d 100644 --- a/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java +++ b/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java @@ -14,13 +14,6 @@ */ package com.amazonaws.examples; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; @@ -33,12 +26,17 @@ import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** - * This demonstrates how to use the {@link CachingMostRecentProvider} backed by a - * {@link MetaStore} and the {@link DirectKmsMaterialProvider} to encrypt - * your data. Before you can use this, you need to set up a table to hold the - * intermediate keys or use --setup mode to construct the table once + * This demonstrates how to use the {@link CachingMostRecentProvider} backed by a {@link MetaStore} + * and the {@link DirectKmsMaterialProvider} to encrypt your data. Before you can use this, you need + * to set up a table to hold the intermediate keys or use --setup mode to construct the table once * and then re-run the example without the --setup mode */ public class MostRecentEncryptedItem { @@ -80,39 +78,54 @@ public static void main(String[] args) throws GeneralSecurityException { } } - public static void encryptRecord(String tableName, String keyTableName, String cmkArn, String materialName, - AmazonDynamoDB ddbClient, AWSKMS kmsClient) throws GeneralSecurityException { + public static void encryptRecord( + String tableName, + String keyTableName, + String cmkArn, + String materialName, + AmazonDynamoDB ddbClient, + AWSKMS kmsClient) + throws GeneralSecurityException { // Sample record to be encrypted final Map record = new HashMap<>(); record.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this")); record.put(SORT_ATTRIBUTE, new AttributeValue().withN("55")); record.put(STRING_FIELD_NAME, new AttributeValue().withS("data")); record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99")); - record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02}))); - record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute - - // Set up our configuration and clients. All of this is thread-safe and can be reused across calls. + record.put( + BINARY_FIELD_NAME, + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02}))); + record.put( + IGNORED_FIELD_NAME, + new AttributeValue().withS("alone")); // We want to ignore this attribute + + // Set up our configuration and clients. All of this is thread-safe and can be reused across + // calls. // Provider Configuration to protect the data keys - // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client `kmsClient` + // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client + // `kmsClient` final DirectKmsMaterialProvider kmsProv = new DirectKmsMaterialProvider(kmsClient, cmkArn); final DynamoDBEncryptor keyEncryptor = DynamoDBEncryptor.getInstance(kmsProv); final MetaStore metaStore = new MetaStore(ddbClient, keyTableName, keyEncryptor); - //Provider configuration to protect the data - final CachingMostRecentProvider cmp = new CachingMostRecentProvider(metaStore, materialName, 60_000); + // Provider configuration to protect the data + final CachingMostRecentProvider cmp = + new CachingMostRecentProvider(metaStore, materialName, 60_000); // Encryptor creation final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); // Information about the context of our data (normally just Table information) - final EncryptionContext encryptionContext = new EncryptionContext.Builder() - .withTableName(tableName) - .withHashKeyName(PARTITION_ATTRIBUTE) - .withRangeKeyName(SORT_ATTRIBUTE) - .build(); + final EncryptionContext encryptionContext = + new EncryptionContext.Builder() + .withTableName(tableName) + .withHashKeyName(PARTITION_ATTRIBUTE) + .withRangeKeyName(SORT_ATTRIBUTE) + .build(); // Describe what actions need to be taken for each attribute final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); - final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); + final EnumSet encryptAndSign = + EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); final Map> actions = new HashMap<>(); for (final String attributeName : record.keySet()) { switch (attributeName) { @@ -133,13 +146,22 @@ public static void encryptRecord(String tableName, String keyTableName, String c // End set-up // Encrypt the plaintext record directly - final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext); + final Map encrypted_record = + encryptor.encryptRecord(record, actions, encryptionContext); // Encrypted record fields change as expected - assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes - assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes - assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated - assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is + assert encrypted_record.get(STRING_FIELD_NAME).getB() + != null; // the encrypted string is stored as bytes + assert encrypted_record.get(NUMBER_FIELD_NAME).getB() + != null; // the encrypted number is stored as bytes + assert !record + .get(BINARY_FIELD_NAME) + .getB() + .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated + assert record + .get(IGNORED_FIELD_NAME) + .getS() + .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is // We could now put the encrypted item to DynamoDB just as we would any other item. // We're skipping it to to keep the example simpler. @@ -148,12 +170,22 @@ public static void encryptRecord(String tableName, String keyTableName, String c System.out.println("Encrypted Record: " + encrypted_record); // Decryption is identical. We'll pretend that we retrieved the record from DynamoDB. - final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext); + final Map decrypted_record = + encryptor.decryptRecord(encrypted_record, actions, encryptionContext); System.out.println("Decrypted Record: " + decrypted_record); // The decrypted fields match the original fields before encryption - assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS()); - assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); - assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); + assert record + .get(STRING_FIELD_NAME) + .getS() + .equals(decrypted_record.get(STRING_FIELD_NAME).getS()); + assert record + .get(NUMBER_FIELD_NAME) + .getN() + .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); + assert record + .get(BINARY_FIELD_NAME) + .getB() + .equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); } } diff --git a/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java index e5f54645..ae2ccb96 100644 --- a/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java +++ b/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java @@ -14,6 +14,11 @@ */ package com.amazonaws.examples; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -21,19 +26,12 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; - import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; - /** - * Example showing use of an AES key for encryption and an HmacSHA256 key for signing. - * For ease of the example, we create new random ones every time. + * Example showing use of an AES key for encryption and an HmacSHA256 key for signing. For ease of + * the example, we create new random ones every time. */ public class SymmetricEncryptedItem { @@ -57,7 +55,8 @@ public static void main(String[] args) throws GeneralSecurityException { encryptRecord(tableName, wrappingKey, signingKey); } - public static void encryptRecord(String tableName, SecretKey wrappingKey, SecretKey signingKey) throws GeneralSecurityException { + public static void encryptRecord(String tableName, SecretKey wrappingKey, SecretKey signingKey) + throws GeneralSecurityException { // Sample record to be encrypted final String partitionKeyName = "partition_attribute"; final String sortKeyName = "sort_attribute"; @@ -66,28 +65,38 @@ public static void encryptRecord(String tableName, SecretKey wrappingKey, Secret record.put(sortKeyName, new AttributeValue().withN("55")); record.put(STRING_FIELD_NAME, new AttributeValue().withS("data")); record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99")); - record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02}))); - record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute - - // Set up our configuration and clients. All of this is thread-safe and can be reused across calls. + record.put( + BINARY_FIELD_NAME, + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02}))); + record.put( + IGNORED_FIELD_NAME, + new AttributeValue().withS("alone")); // We want to ignore this attribute + + // Set up our configuration and clients. All of this is thread-safe and can be reused across + // calls. // Provider Configuration - final WrappedMaterialsProvider cmp = new WrappedMaterialsProvider(wrappingKey, wrappingKey, signingKey); + final WrappedMaterialsProvider cmp = + new WrappedMaterialsProvider(wrappingKey, wrappingKey, signingKey); // While the wrappedMaterialsProvider is better as it uses a unique encryption key per record, - // many existing systems use the SymmetricStaticProvider which always uses the same encryption key. - // final SymmetricStaticProvider cmp = new SymmetricStaticProvider(encryptionKey, signingKey); + // many existing systems use the SymmetricStaticProvider which always uses the same encryption + // key. + // final SymmetricStaticProvider cmp = new SymmetricStaticProvider(encryptionKey, + // signingKey); // Encryptor creation final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); // Information about the context of our data (normally just Table information) - final EncryptionContext encryptionContext = new EncryptionContext.Builder() - .withTableName(tableName) - .withHashKeyName(partitionKeyName) - .withRangeKeyName(sortKeyName) - .build(); + final EncryptionContext encryptionContext = + new EncryptionContext.Builder() + .withTableName(tableName) + .withHashKeyName(partitionKeyName) + .withRangeKeyName(sortKeyName) + .build(); // Describe what actions need to be taken for each attribute final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); - final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); + final EnumSet encryptAndSign = + EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); final Map> actions = new HashMap<>(); for (final String attributeName : record.keySet()) { switch (attributeName) { @@ -108,13 +117,22 @@ public static void encryptRecord(String tableName, SecretKey wrappingKey, Secret // End set-up // Encrypt the plaintext record directly - final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext); + final Map encrypted_record = + encryptor.encryptRecord(record, actions, encryptionContext); // Encrypted record fields change as expected - assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes - assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes - assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated - assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is + assert encrypted_record.get(STRING_FIELD_NAME).getB() + != null; // the encrypted string is stored as bytes + assert encrypted_record.get(NUMBER_FIELD_NAME).getB() + != null; // the encrypted number is stored as bytes + assert !record + .get(BINARY_FIELD_NAME) + .getB() + .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated + assert record + .get(IGNORED_FIELD_NAME) + .getS() + .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is // We could now put the encrypted item to DynamoDB just as we would any other item. // We're skipping it to to keep the example simpler. @@ -123,12 +141,22 @@ public static void encryptRecord(String tableName, SecretKey wrappingKey, Secret System.out.println("Encrypted Record: " + encrypted_record); // Decryption is identical. We'll pretend that we retrieved the record from DynamoDB. - final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext); + final Map decrypted_record = + encryptor.decryptRecord(encrypted_record, actions, encryptionContext); System.out.println("Decrypted Record: " + decrypted_record); // The decrypted fields match the original fields before encryption - assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS()); - assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); - assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); + assert record + .get(STRING_FIELD_NAME) + .getS() + .equals(decrypted_record.get(STRING_FIELD_NAME).getS()); + assert record + .get(NUMBER_FIELD_NAME) + .getN() + .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN()); + assert record + .get(BINARY_FIELD_NAME) + .getB() + .equals(decrypted_record.get(BINARY_FIELD_NAME).getB()); } } diff --git a/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java b/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java index 3ead80f5..31ee3506 100644 --- a/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java +++ b/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java @@ -3,22 +3,21 @@ package com.amazonaws.examples; -import org.testng.annotations.Test; - import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; +import org.testng.annotations.Test; public class AsymmetricEncryptedItemTest { - private static final String TABLE_NAME = "java-ddbec-test-table-asym-example"; + private static final String TABLE_NAME = "java-ddbec-test-table-asym-example"; - @Test - public void testEncryptAndDecrypt() throws GeneralSecurityException { - final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - final KeyPair wrappingKeys = keyGen.generateKeyPair(); - final KeyPair signingKeys = keyGen.generateKeyPair(); + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + final KeyPair wrappingKeys = keyGen.generateKeyPair(); + final KeyPair signingKeys = keyGen.generateKeyPair(); - AsymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKeys, signingKeys); - } + AsymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKeys, signingKeys); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java index 43dc5acf..537e664e 100644 --- a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java +++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java @@ -3,21 +3,20 @@ package com.amazonaws.examples; +import static com.amazonaws.examples.TestUtils.US_WEST_2; +import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID; + import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; -import org.testng.annotations.Test; - import java.security.GeneralSecurityException; - -import static com.amazonaws.examples.TestUtils.US_WEST_2; -import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID; +import org.testng.annotations.Test; public class AwsKmsEncryptedItemIT { - private static final String TABLE_NAME = "java-ddbec-test-table-kms-item-example"; + private static final String TABLE_NAME = "java-ddbec-test-table-kms-item-example"; - @Test - public void testEncryptAndDecrypt() throws GeneralSecurityException { - final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); - AwsKmsEncryptedItem.encryptRecord(TABLE_NAME, US_WEST_2_KEY_ID, kms); - } + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); + AwsKmsEncryptedItem.encryptRecord(TABLE_NAME, US_WEST_2_KEY_ID, kms); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java index c2e31831..4dc2c884 100644 --- a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java +++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java @@ -3,12 +3,6 @@ package com.amazonaws.examples; -import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; -import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; -import com.amazonaws.services.kms.AWSKMS; -import com.amazonaws.services.kms.AWSKMSClientBuilder; -import org.testng.annotations.Test; - import static com.amazonaws.examples.AwsKmsEncryptedObject.EXAMPLE_TABLE_NAME; import static com.amazonaws.examples.AwsKmsEncryptedObject.PARTITION_ATTRIBUTE; import static com.amazonaws.examples.AwsKmsEncryptedObject.SORT_ATTRIBUTE; @@ -16,16 +10,22 @@ import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID; import static com.amazonaws.examples.TestUtils.createDDBTable; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; +import com.amazonaws.services.kms.AWSKMS; +import com.amazonaws.services.kms.AWSKMSClientBuilder; +import org.testng.annotations.Test; + public class AwsKmsEncryptedObjectIT { - @Test - public void testEncryptAndDecrypt() { - final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); - final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); + @Test + public void testEncryptAndDecrypt() { + final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); + final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); - // Create the table under test - createDDBTable(ddb, EXAMPLE_TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); + // Create the table under test + createDDBTable(ddb, EXAMPLE_TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); - AwsKmsEncryptedObject.encryptRecord(US_WEST_2_KEY_ID, ddb, kms); - } + AwsKmsEncryptedObject.encryptRecord(US_WEST_2_KEY_ID, ddb, kms); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java index 28ba4f34..664b7bce 100644 --- a/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java +++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java @@ -3,18 +3,17 @@ package com.amazonaws.examples; -import org.testng.annotations.Test; - -import java.security.GeneralSecurityException; - 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 AwsKmsMultiRegionKeyIT { - private static final String TABLE_NAME = "ddbec-mrk-testing"; + private static final String TABLE_NAME = "ddbec-mrk-testing"; - @Test - public void testEncryptAndDecrypt() throws GeneralSecurityException { - AwsKmsMultiRegionKey.encryptRecord(TABLE_NAME, US_EAST_1_MRK_KEY_ID, US_WEST_2_MRK_KEY_ID); - } + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + AwsKmsMultiRegionKey.encryptRecord(TABLE_NAME, US_EAST_1_MRK_KEY_ID, US_WEST_2_MRK_KEY_ID); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java b/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java index fa32dd88..4dbc30b6 100644 --- a/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java +++ b/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java @@ -3,15 +3,6 @@ package com.amazonaws.examples; -import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; -import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; - -import com.amazonaws.services.kms.AWSKMS; -import com.amazonaws.services.kms.AWSKMSClientBuilder; -import org.testng.annotations.Test; - -import java.security.GeneralSecurityException; - import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.PARTITION_ATTRIBUTE; import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.SORT_ATTRIBUTE; import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.TABLE_NAME_TO_OVERRIDE; @@ -19,17 +10,25 @@ import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID; import static com.amazonaws.examples.TestUtils.createDDBTable; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; +import com.amazonaws.services.kms.AWSKMS; +import com.amazonaws.services.kms.AWSKMSClientBuilder; +import java.security.GeneralSecurityException; +import org.testng.annotations.Test; + public class EncryptionContextOverridesWithDynamoDBMapperIT { - private static final String OVERRIDE_TABLE_NAME = "java-ddbec-test-table-encctx-override-example"; + private static final String OVERRIDE_TABLE_NAME = "java-ddbec-test-table-encctx-override-example"; - @Test - public void testEncryptAndDecrypt() throws GeneralSecurityException { - final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); - final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); + final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); - // Create the table under test - createDDBTable(ddb, TABLE_NAME_TO_OVERRIDE, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); + // Create the table under test + createDDBTable(ddb, TABLE_NAME_TO_OVERRIDE, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); - EncryptionContextOverridesWithDynamoDBMapper.encryptRecord(US_WEST_2_KEY_ID, OVERRIDE_TABLE_NAME, ddb, kms); - } + EncryptionContextOverridesWithDynamoDBMapper.encryptRecord( + US_WEST_2_KEY_ID, OVERRIDE_TABLE_NAME, ddb, kms); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java b/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java index 420f0a7b..3c5e1f55 100644 --- a/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java +++ b/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java @@ -3,36 +3,36 @@ package com.amazonaws.examples; +import static com.amazonaws.examples.MostRecentEncryptedItem.PARTITION_ATTRIBUTE; +import static com.amazonaws.examples.MostRecentEncryptedItem.SORT_ATTRIBUTE; +import static com.amazonaws.examples.TestUtils.*; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store.MetaStore; import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; -import org.testng.annotations.Test; - import java.security.GeneralSecurityException; - -import static com.amazonaws.examples.MostRecentEncryptedItem.PARTITION_ATTRIBUTE; -import static com.amazonaws.examples.MostRecentEncryptedItem.SORT_ATTRIBUTE; -import static com.amazonaws.examples.TestUtils.*; +import org.testng.annotations.Test; public class MostRecentEncryptedItemIT { - private static final String TABLE_NAME = "java-ddbec-test-table-mostrecent-example"; - private static final String KEY_TABLE_NAME = "java-ddbec-test-table-mostrecent-example-keys"; - private static final String MATERIAL_NAME = "testMaterial"; + private static final String TABLE_NAME = "java-ddbec-test-table-mostrecent-example"; + private static final String KEY_TABLE_NAME = "java-ddbec-test-table-mostrecent-example-keys"; + private static final String MATERIAL_NAME = "testMaterial"; - @Test - public void testEncryptAndDecrypt() throws GeneralSecurityException { - final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); - final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build(); + final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); - // Create the key table under test - MetaStore.createTable(ddb, KEY_TABLE_NAME, new ProvisionedThroughput(1L, 1L)); + // Create the key table under test + MetaStore.createTable(ddb, KEY_TABLE_NAME, new ProvisionedThroughput(1L, 1L)); - // Create the table under test - createDDBTable(ddb, TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); + // Create the table under test + createDDBTable(ddb, TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); - MostRecentEncryptedItem.encryptRecord(TABLE_NAME, KEY_TABLE_NAME, US_WEST_2_KEY_ID, MATERIAL_NAME, ddb, kms); - } + MostRecentEncryptedItem.encryptRecord( + TABLE_NAME, KEY_TABLE_NAME, US_WEST_2_KEY_ID, MATERIAL_NAME, ddb, kms); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java b/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java index 54c14353..acf54d6c 100644 --- a/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java +++ b/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java @@ -3,26 +3,25 @@ package com.amazonaws.examples; -import org.testng.annotations.Test; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.security.GeneralSecurityException; import java.security.SecureRandom; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.testng.annotations.Test; public class SymmetricEncryptedItemTest { - private static final String TABLE_NAME = "java-ddbec-test-table-sym-example"; + private static final String TABLE_NAME = "java-ddbec-test-table-sym-example"; - @Test - public void testEncryptAndDecrypt() throws GeneralSecurityException { - final SecureRandom secureRandom = new SecureRandom(); - byte[] rawAes = new byte[32]; - byte[] rawHmac = new byte[32]; - secureRandom.nextBytes(rawAes); - secureRandom.nextBytes(rawHmac); - final SecretKey wrappingKey = new SecretKeySpec(rawAes, "AES"); - final SecretKey signingKey = new SecretKeySpec(rawHmac, "HmacSHA256"); + @Test + public void testEncryptAndDecrypt() throws GeneralSecurityException { + final SecureRandom secureRandom = new SecureRandom(); + byte[] rawAes = new byte[32]; + byte[] rawHmac = new byte[32]; + secureRandom.nextBytes(rawAes); + secureRandom.nextBytes(rawHmac); + final SecretKey wrappingKey = new SecretKeySpec(rawAes, "AES"); + final SecretKey signingKey = new SecretKeySpec(rawHmac, "HmacSHA256"); - SymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKey, signingKey); - } + SymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKey, signingKey); + } } diff --git a/examples/src/test/java/com/amazonaws/examples/TestUtils.java b/examples/src/test/java/com/amazonaws/examples/TestUtils.java index 349e1a41..abc43913 100644 --- a/examples/src/test/java/com/amazonaws/examples/TestUtils.java +++ b/examples/src/test/java/com/amazonaws/examples/TestUtils.java @@ -1,43 +1,55 @@ package com.amazonaws.examples; +import static com.amazonaws.examples.AwsKmsEncryptedObject.*; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.model.*; - import java.util.ArrayList; -import static com.amazonaws.examples.AwsKmsEncryptedObject.*; - public class TestUtils { - private TestUtils() { - throw new UnsupportedOperationException( - "This class exists to hold static resources and cannot be instantiated." - ); - } - - /** - * These special test keys have been configured to allow Encrypt, Decrypt, and GenerateDataKey operations from any - * AWS principal and should be used when adding new KMS tests. - * - * This should go without saying, but never use these keys for production purposes (as anyone in the world can - * decrypt data encrypted using them). - */ - public static final String US_WEST_2_KEY_ID = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"; - public static final String US_WEST_2 = "us-west-2"; - public static final String US_EAST_1_MRK_KEY_ID = "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; - public static final String US_WEST_2_MRK_KEY_ID = "arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; - - public static void createDDBTable(AmazonDynamoDB ddb, String tableName, String partitionName, String sortName) { - ArrayList attrDef = new ArrayList(); - attrDef.add(new AttributeDefinition().withAttributeName(partitionName).withAttributeType(ScalarAttributeType.S)); - attrDef.add(new AttributeDefinition().withAttributeName(sortName).withAttributeType(ScalarAttributeType.N)); - - ArrayList keySchema = new ArrayList(); - keySchema.add(new KeySchemaElement().withAttributeName(partitionName).withKeyType(KeyType.HASH)); - keySchema.add(new KeySchemaElement().withAttributeName(sortName).withKeyType(KeyType.RANGE)); - - ddb.createTable(new CreateTableRequest().withTableName(tableName) - .withAttributeDefinitions(attrDef) - .withKeySchema(keySchema) - .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); - } + private TestUtils() { + throw new UnsupportedOperationException( + "This class exists to hold static resources and cannot be instantiated."); + } + + /** + * These special test keys have been configured to allow Encrypt, Decrypt, and GenerateDataKey + * operations from any AWS principal and should be used when adding new KMS tests. + * + *

This should go without saying, but never use these keys for production purposes (as anyone + * in the world can decrypt data encrypted using them). + */ + public static final String US_WEST_2_KEY_ID = + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"; + + public static final String US_WEST_2 = "us-west-2"; + public static final String US_EAST_1_MRK_KEY_ID = + "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; + public static final String US_WEST_2_MRK_KEY_ID = + "arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; + + public static void createDDBTable( + AmazonDynamoDB ddb, String tableName, String partitionName, String sortName) { + ArrayList attrDef = new ArrayList(); + attrDef.add( + new AttributeDefinition() + .withAttributeName(partitionName) + .withAttributeType(ScalarAttributeType.S)); + attrDef.add( + new AttributeDefinition() + .withAttributeName(sortName) + .withAttributeType(ScalarAttributeType.N)); + + ArrayList keySchema = new ArrayList(); + keySchema.add( + new KeySchemaElement().withAttributeName(partitionName).withKeyType(KeyType.HASH)); + keySchema.add(new KeySchemaElement().withAttributeName(sortName).withKeyType(KeyType.RANGE)); + + ddb.createTable( + new CreateTableRequest() + .withTableName(tableName) + .withAttributeDefinitions(attrDef) + .withKeySchema(keySchema) + .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); + } } diff --git a/pom.xml b/pom.xml index 238301fb..1ce12fac 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,16 @@ true + + + com.coveo + fmt-maven-plugin + 2.10 + + + + + diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java index 1727a140..12d8a34f 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java @@ -14,13 +14,6 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.SaveBehavior; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingsRegistry.Mapping; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingsRegistry.Mappings; @@ -33,259 +26,260 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.TableAadOverride; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * Encrypts all non-key fields prior to storing them in DynamoDB. - * This must be used with {@link SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}. + * Encrypts all non-key fields prior to storing them in DynamoDB. This must be used with {@link + * SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}. * - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

+ *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * - * @author Greg Rubin + * @author Greg Rubin */ public class AttributeEncryptor implements AttributeTransformer { - private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class); - private final DynamoDBEncryptor encryptor; - private final Map, ModelClassMetadata> metadataCache = new ConcurrentHashMap<>(); - - public AttributeEncryptor(final DynamoDBEncryptor encryptor) { - this.encryptor = encryptor; + private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class); + private final DynamoDBEncryptor encryptor; + private final Map, ModelClassMetadata> metadataCache = new ConcurrentHashMap<>(); + + public AttributeEncryptor(final DynamoDBEncryptor encryptor) { + this.encryptor = encryptor; + } + + public AttributeEncryptor(final EncryptionMaterialsProvider encryptionMaterialsProvider) { + encryptor = DynamoDBEncryptor.getInstance(encryptionMaterialsProvider); + } + + public DynamoDBEncryptor getEncryptor() { + return encryptor; + } + + @Override + public Map transform(final Parameters parameters) { + // one map of attributeFlags per model class + final ModelClassMetadata metadata = getModelClassMetadata(parameters); + + final Map attributeValues = parameters.getAttributeValues(); + // If this class is marked as "DoNotTouch" then we know our encryptor will not change it at all + // so we may as well fast-return and do nothing. This also avoids emitting errors when they + // would not apply. + if (metadata.doNotTouch) { + return attributeValues; } - public AttributeEncryptor(final EncryptionMaterialsProvider encryptionMaterialsProvider) { - encryptor = DynamoDBEncryptor.getInstance(encryptionMaterialsProvider); + // When AttributeEncryptor is used without SaveBehavior.PUT or CLOBBER, it is trying to + // transform only a subset + // of the actual fields stored in DynamoDB. This means that the generated signature will not + // cover any + // unmodified fields. Thus, upon untransform, the signature verification will fail as it won't + // cover all + // expected fields. + if (parameters.isPartialUpdate()) { + throw new DynamoDBMappingException( + "Use of AttributeEncryptor without SaveBehavior.PUT or SaveBehavior.CLOBBER is an error " + + "and can result in data-corruption. This occured while trying to save " + + parameters.getModelClass()); } - public DynamoDBEncryptor getEncryptor() { - return encryptor; + try { + return encryptor.encryptRecord( + attributeValues, metadata.getEncryptionFlags(), paramsToContext(parameters)); + } catch (Exception ex) { + throw new DynamoDBMappingException(ex); } + } - @Override - public Map transform(final Parameters parameters) { - // one map of attributeFlags per model class - final ModelClassMetadata metadata = getModelClassMetadata(parameters); - - final Map attributeValues = parameters.getAttributeValues(); - // If this class is marked as "DoNotTouch" then we know our encryptor will not change it at all - // so we may as well fast-return and do nothing. This also avoids emitting errors when they would not apply. - if (metadata.doNotTouch) { - return attributeValues; - } - - // When AttributeEncryptor is used without SaveBehavior.PUT or CLOBBER, it is trying to transform only a subset - // of the actual fields stored in DynamoDB. This means that the generated signature will not cover any - // unmodified fields. Thus, upon untransform, the signature verification will fail as it won't cover all - // expected fields. - if (parameters.isPartialUpdate()) { - throw new DynamoDBMappingException( - "Use of AttributeEncryptor without SaveBehavior.PUT or SaveBehavior.CLOBBER is an error " + - "and can result in data-corruption. This occured while trying to save " + - parameters.getModelClass()); - } + @Override + public Map untransform(final Parameters parameters) { + final Map> attributeFlags = getEncryptionFlags(parameters); - try { - return encryptor.encryptRecord( - attributeValues, - metadata.getEncryptionFlags(), - paramsToContext(parameters)); - } catch (Exception ex) { - throw new DynamoDBMappingException(ex); - } + try { + return encryptor.decryptRecord( + parameters.getAttributeValues(), attributeFlags, paramsToContext(parameters)); + } catch (Exception ex) { + throw new DynamoDBMappingException(ex); } - - @Override - public Map untransform(final Parameters parameters) { - final Map> attributeFlags = getEncryptionFlags(parameters); - - try { - return encryptor.decryptRecord( - parameters.getAttributeValues(), - attributeFlags, - paramsToContext(parameters)); - } catch (Exception ex) { - throw new DynamoDBMappingException(ex); - } + } + + /* + * For any attributes we see from DynamoDB that aren't modeled in the mapper class, + * we either ignore them (the default behavior), or include them for encryption/signing + * based on the presence of the @HandleUnknownAttributes annotation (unless the class + * has @DoNotTouch, then we don't include them). + */ + private Map> getEncryptionFlags(final Parameters parameters) { + final ModelClassMetadata metadata = getModelClassMetadata(parameters); + + // If the class is annotated with @DoNotTouch, then none of the attributes are + // encrypted or signed, so we don't need to bother looking for unknown attributes. + if (metadata.getDoNotTouch()) { + return metadata.getEncryptionFlags(); } - /* - * For any attributes we see from DynamoDB that aren't modeled in the mapper class, - * we either ignore them (the default behavior), or include them for encryption/signing - * based on the presence of the @HandleUnknownAttributes annotation (unless the class - * has @DoNotTouch, then we don't include them). - */ - private Map> getEncryptionFlags(final Parameters parameters) { - final ModelClassMetadata metadata = getModelClassMetadata(parameters); - - // If the class is annotated with @DoNotTouch, then none of the attributes are - // encrypted or signed, so we don't need to bother looking for unknown attributes. - if (metadata.getDoNotTouch()) { - return metadata.getEncryptionFlags(); - } + final Set unknownAttributeBehavior = metadata.getUnknownAttributeBehavior(); + final Map> attributeFlags = new HashMap<>(); + attributeFlags.putAll(metadata.getEncryptionFlags()); - final Set unknownAttributeBehavior = metadata.getUnknownAttributeBehavior(); - final Map> attributeFlags = new HashMap<>(); - attributeFlags.putAll(metadata.getEncryptionFlags()); - - for (final String attributeName : parameters.getAttributeValues().keySet()) { - if (!attributeFlags.containsKey(attributeName) && - !encryptor.getSignatureFieldName().equals(attributeName) && - !encryptor.getMaterialDescriptionFieldName().equals(attributeName)) { + for (final String attributeName : parameters.getAttributeValues().keySet()) { + if (!attributeFlags.containsKey(attributeName) + && !encryptor.getSignatureFieldName().equals(attributeName) + && !encryptor.getMaterialDescriptionFieldName().equals(attributeName)) { - attributeFlags.put(attributeName, unknownAttributeBehavior); - } - } - - return attributeFlags; + attributeFlags.put(attributeName, unknownAttributeBehavior); + } } - private ModelClassMetadata getModelClassMetadata(Parameters parameters) { - // Due to the lack of explicit synchronization, it is possible that - // elements in the cache will be added multiple times. Since they will - // all be identical, this is okay. Avoiding explicit synchronization - // means that in the general (retrieval) case, should never block and - // should be extremely fast. - final Class clazz = parameters.getModelClass(); - ModelClassMetadata metadata = metadataCache.get(clazz); - - if (metadata == null) { - Map> attributeFlags = new HashMap<>(); - - final boolean handleUnknownAttributes = handleUnknownAttributes(clazz); - final EnumSet unknownAttributeBehavior = EnumSet.noneOf(EncryptionFlags.class); - - if (shouldTouch(clazz)) { - Mappings mappings = DynamoDBMappingsRegistry.instance().mappingsOf(clazz); - - for (Mapping mapping : mappings.getMappings()) { - final EnumSet flags = EnumSet.noneOf(EncryptionFlags.class); - StandardAnnotationMaps.FieldMap fieldMap = StandardAnnotationMaps.of(mapping.getter(), null); - if (shouldTouch(fieldMap)) { - if (shouldEncryptAttribute(clazz, mapping, fieldMap)) { - flags.add(EncryptionFlags.ENCRYPT); - } - flags.add(EncryptionFlags.SIGN); - } - attributeFlags.put(mapping.getAttributeName(), Collections.unmodifiableSet(flags)); - } - - if (handleUnknownAttributes) { - unknownAttributeBehavior.add(EncryptionFlags.SIGN); - - if (shouldEncrypt(clazz)) { - unknownAttributeBehavior.add(EncryptionFlags.ENCRYPT); - } - } + return attributeFlags; + } + + private ModelClassMetadata getModelClassMetadata(Parameters parameters) { + // Due to the lack of explicit synchronization, it is possible that + // elements in the cache will be added multiple times. Since they will + // all be identical, this is okay. Avoiding explicit synchronization + // means that in the general (retrieval) case, should never block and + // should be extremely fast. + final Class clazz = parameters.getModelClass(); + ModelClassMetadata metadata = metadataCache.get(clazz); + + if (metadata == null) { + Map> attributeFlags = new HashMap<>(); + + final boolean handleUnknownAttributes = handleUnknownAttributes(clazz); + final EnumSet unknownAttributeBehavior = + EnumSet.noneOf(EncryptionFlags.class); + + if (shouldTouch(clazz)) { + Mappings mappings = DynamoDBMappingsRegistry.instance().mappingsOf(clazz); + + for (Mapping mapping : mappings.getMappings()) { + final EnumSet flags = EnumSet.noneOf(EncryptionFlags.class); + StandardAnnotationMaps.FieldMap fieldMap = + StandardAnnotationMaps.of(mapping.getter(), null); + if (shouldTouch(fieldMap)) { + if (shouldEncryptAttribute(clazz, mapping, fieldMap)) { + flags.add(EncryptionFlags.ENCRYPT); } - - metadata = new ModelClassMetadata(Collections.unmodifiableMap(attributeFlags), doNotTouch(clazz), - Collections.unmodifiableSet(unknownAttributeBehavior)); - metadataCache.put(clazz, metadata); + flags.add(EncryptionFlags.SIGN); + } + attributeFlags.put(mapping.getAttributeName(), Collections.unmodifiableSet(flags)); } - return metadata; - } - - /** - * @return True if {@link DoNotTouch} is not present on the class level. False otherwise - */ - private boolean shouldTouch(Class clazz) { - return !doNotTouch(clazz); - } - - /** - * @return True if {@link DoNotTouch} is not present on the getter level. False otherwise. - */ - private boolean shouldTouch(StandardAnnotationMaps.FieldMap fieldMap) { - return !doNotTouch(fieldMap); - } - - /** - * @return True if {@link DoNotTouch} IS present on the class level. False otherwise. - */ - private boolean doNotTouch(Class clazz) { - return clazz.isAnnotationPresent(DoNotTouch.class); - } - - /** - * @return True if {@link DoNotTouch} IS present on the getter level. False otherwise. - */ - private boolean doNotTouch(StandardAnnotationMaps.FieldMap fieldMap) { - return fieldMap.actualOf(DoNotTouch.class) != null; - } - - /** - * @return True if {@link DoNotEncrypt} is NOT present on the class level. False otherwise. - */ - private boolean shouldEncrypt(Class clazz) { - return !doNotEncrypt(clazz); - } - /** - * @return True if {@link DoNotEncrypt} IS present on the class level. False otherwise. - */ - private boolean doNotEncrypt(Class clazz) { - return clazz.isAnnotationPresent(DoNotEncrypt.class); - } + if (handleUnknownAttributes) { + unknownAttributeBehavior.add(EncryptionFlags.SIGN); - /** - * @return True if {@link DoNotEncrypt} IS present on the getter level. False otherwise. - */ - private boolean doNotEncrypt(StandardAnnotationMaps.FieldMap fieldMap) { - return fieldMap.actualOf(DoNotEncrypt.class) != null; + if (shouldEncrypt(clazz)) { + unknownAttributeBehavior.add(EncryptionFlags.ENCRYPT); + } + } + } + + metadata = + new ModelClassMetadata( + Collections.unmodifiableMap(attributeFlags), + doNotTouch(clazz), + Collections.unmodifiableSet(unknownAttributeBehavior)); + metadataCache.put(clazz, metadata); } - - /** - * @return True if the attribute should be encrypted, false otherwise. - */ - private boolean shouldEncryptAttribute( - final Class clazz, - final Mapping mapping, - final StandardAnnotationMaps.FieldMap fieldMap) { - - return !(doNotEncrypt(clazz) || doNotEncrypt(fieldMap) || mapping.isPrimaryKey() || mapping.isVersion()); + return metadata; + } + + /** @return True if {@link DoNotTouch} is not present on the class level. False otherwise */ + private boolean shouldTouch(Class clazz) { + return !doNotTouch(clazz); + } + + /** @return True if {@link DoNotTouch} is not present on the getter level. False otherwise. */ + private boolean shouldTouch(StandardAnnotationMaps.FieldMap fieldMap) { + return !doNotTouch(fieldMap); + } + + /** @return True if {@link DoNotTouch} IS present on the class level. False otherwise. */ + private boolean doNotTouch(Class clazz) { + return clazz.isAnnotationPresent(DoNotTouch.class); + } + + /** @return True if {@link DoNotTouch} IS present on the getter level. False otherwise. */ + private boolean doNotTouch(StandardAnnotationMaps.FieldMap fieldMap) { + return fieldMap.actualOf(DoNotTouch.class) != null; + } + + /** @return True if {@link DoNotEncrypt} is NOT present on the class level. False otherwise. */ + private boolean shouldEncrypt(Class clazz) { + return !doNotEncrypt(clazz); + } + + /** @return True if {@link DoNotEncrypt} IS present on the class level. False otherwise. */ + private boolean doNotEncrypt(Class clazz) { + return clazz.isAnnotationPresent(DoNotEncrypt.class); + } + + /** @return True if {@link DoNotEncrypt} IS present on the getter level. False otherwise. */ + private boolean doNotEncrypt(StandardAnnotationMaps.FieldMap fieldMap) { + return fieldMap.actualOf(DoNotEncrypt.class) != null; + } + + /** @return True if the attribute should be encrypted, false otherwise. */ + private boolean shouldEncryptAttribute( + final Class clazz, + final Mapping mapping, + final StandardAnnotationMaps.FieldMap fieldMap) { + + return !(doNotEncrypt(clazz) + || doNotEncrypt(fieldMap) + || mapping.isPrimaryKey() + || mapping.isVersion()); + } + + private static EncryptionContext paramsToContext(Parameters params) { + final Class clazz = params.getModelClass(); + final TableAadOverride override = clazz.getAnnotation(TableAadOverride.class); + final String tableName = ((override == null) ? params.getTableName() : override.tableName()); + + return new EncryptionContext.Builder() + .withHashKeyName(params.getHashKeyName()) + .withRangeKeyName(params.getRangeKeyName()) + .withTableName(tableName) + .withModeledClass(params.getModelClass()) + .withAttributeValues(params.getAttributeValues()) + .build(); + } + + private boolean handleUnknownAttributes(Class clazz) { + return clazz.getAnnotation(HandleUnknownAttributes.class) != null; + } + + private static class ModelClassMetadata { + private final Map> encryptionFlags; + private final boolean doNotTouch; + private final Set unknownAttributeBehavior; + + public ModelClassMetadata( + Map> encryptionFlags, + boolean doNotTouch, + Set unknownAttributeBehavior) { + this.encryptionFlags = encryptionFlags; + this.doNotTouch = doNotTouch; + this.unknownAttributeBehavior = unknownAttributeBehavior; } - private static EncryptionContext paramsToContext(Parameters params) { - final Class clazz = params.getModelClass(); - final TableAadOverride override = clazz.getAnnotation(TableAadOverride.class); - final String tableName = ((override == null) ? params.getTableName() : override.tableName()); - - return new EncryptionContext.Builder() - .withHashKeyName(params.getHashKeyName()) - .withRangeKeyName(params.getRangeKeyName()) - .withTableName(tableName) - .withModeledClass(params.getModelClass()) - .withAttributeValues(params.getAttributeValues()).build(); + public Map> getEncryptionFlags() { + return encryptionFlags; } - private boolean handleUnknownAttributes(Class clazz) { - return clazz.getAnnotation(HandleUnknownAttributes.class) != null; + public boolean getDoNotTouch() { + return doNotTouch; } - private static class ModelClassMetadata { - private final Map> encryptionFlags; - private final boolean doNotTouch; - private final Set unknownAttributeBehavior; - - public ModelClassMetadata(Map> encryptionFlags, - boolean doNotTouch, Set unknownAttributeBehavior) { - this.encryptionFlags = encryptionFlags; - this.doNotTouch = doNotTouch; - this.unknownAttributeBehavior = unknownAttributeBehavior; - } - - public Map> getEncryptionFlags() { - return encryptionFlags; - } - - public boolean getDoNotTouch() { - return doNotTouch; - } - - public Set getUnknownAttributeBehavior() { - return unknownAttributeBehavior; - } + public Set getUnknownAttributeBehavior() { + return unknownAttributeBehavior; } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java index e42e6e97..5445030f 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java @@ -19,7 +19,6 @@ import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; - import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -27,120 +26,104 @@ import javax.crypto.SecretKey; /** - * Identifies keys which should not be used directly with {@link Cipher} but - * instead contain their own cryptographic logic. This can be used to wrap more - * complex logic, HSM integration, or service-calls. - * - *

- * Most delegated keys will only support a subset of these operations. (For - * example, AES keys will generally not support {@link #sign(byte[], String)} or - * {@link #verify(byte[], byte[], String)} and HMAC keys will generally not - * support anything except sign and verify.) - * {@link UnsupportedOperationException} should be thrown in these cases. - * - * @author Greg Rubin + * Identifies keys which should not be used directly with {@link Cipher} but instead contain their + * own cryptographic logic. This can be used to wrap more complex logic, HSM integration, or + * service-calls. + * + *

Most delegated keys will only support a subset of these operations. (For example, AES keys + * will generally not support {@link #sign(byte[], String)} or {@link #verify(byte[], byte[], + * String)} and HMAC keys will generally not support anything except sign and + * verify.) {@link UnsupportedOperationException} should be thrown in these cases. + * + * @author Greg Rubin */ public interface DelegatedKey extends SecretKey { - /** - * Encrypts the provided plaintext and returns a byte-array containing the ciphertext. - * - * @param plainText - * @param additionalAssociatedData - * Optional additional data which must then also be provided for successful - * decryption. Both null and arrays of length 0 are treated identically. - * Not all keys will support this parameter. - * @param algorithm - * the transformation to be used when encrypting the data - * @return ciphertext the ciphertext produced by this encryption operation - * @throws UnsupportedOperationException - * if encryption is not supported or if additionalAssociatedData is - * provided, but not supported. - */ - public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException; + /** + * Encrypts the provided plaintext and returns a byte-array containing the ciphertext. + * + * @param plainText + * @param additionalAssociatedData Optional additional data which must then also be provided for + * successful decryption. Both null and arrays of length 0 are treated + * identically. Not all keys will support this parameter. + * @param algorithm the transformation to be used when encrypting the data + * @return ciphertext the ciphertext produced by this encryption operation + * @throws UnsupportedOperationException if encryption is not supported or if + * additionalAssociatedData is provided, but not supported. + */ + public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm) + throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, + NoSuchAlgorithmException, NoSuchPaddingException; - /** - * Decrypts the provided ciphertext and returns a byte-array containing the - * plaintext. - * - * @param cipherText - * @param additionalAssociatedData - * Optional additional data which was provided during encryption. - * Both null and arrays of length 0 are treated - * identically. Not all keys will support this parameter. - * @param algorithm - * the transformation to be used when decrypting the data - * @return plaintext the result of decrypting the input ciphertext - * @throws UnsupportedOperationException - * if decryption is not supported or if - * additionalAssociatedData is provided, but not - * supported. - */ - public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidAlgorithmParameterException; + /** + * Decrypts the provided ciphertext and returns a byte-array containing the plaintext. + * + * @param cipherText + * @param additionalAssociatedData Optional additional data which was provided during encryption. + * Both null and arrays of length 0 are treated identically. Not all keys will + * support this parameter. + * @param algorithm the transformation to be used when decrypting the data + * @return plaintext the result of decrypting the input ciphertext + * @throws UnsupportedOperationException if decryption is not supported or if + * additionalAssociatedData is provided, but not supported. + */ + public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm) + throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, + NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException; - /** - * Wraps (encrypts) the provided key to make it safe for - * storage or transmission. - * - * @param key - * @param additionalAssociatedData - * Optional additional data which must then also be provided for - * successful unwrapping. Both null and arrays of - * length 0 are treated identically. Not all keys will support - * this parameter. - * @param algorithm - * the transformation to be used when wrapping the key - * @return the wrapped key - * @throws UnsupportedOperationException - * if wrapping is not supported or if - * additionalAssociatedData is provided, but not - * supported. - */ - public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) throws InvalidKeyException, - NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException; + /** + * Wraps (encrypts) the provided key to make it safe for storage or transmission. + * + * @param key + * @param additionalAssociatedData Optional additional data which must then also be provided for + * successful unwrapping. Both null and arrays of length 0 are treated + * identically. Not all keys will support this parameter. + * @param algorithm the transformation to be used when wrapping the key + * @return the wrapped key + * @throws UnsupportedOperationException if wrapping is not supported or if + * additionalAssociatedData is provided, but not supported. + */ + public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, + IllegalBlockSizeException; - /** - * Unwraps (decrypts) the provided wrappedKey to recover the - * original key. - * - * @param wrappedKey - * @param additionalAssociatedData - * Optional additional data which was provided during wrapping. - * Both null and arrays of length 0 are treated - * identically. Not all keys will support this parameter. - * @param algorithm - * the transformation to be used when unwrapping the key - * @return the unwrapped key - * @throws UnsupportedOperationException - * if wrapping is not supported or if - * additionalAssociatedData is provided, but not - * supported. - */ - public Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType, - byte[] additionalAssociatedData, String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException; + /** + * Unwraps (decrypts) the provided wrappedKey to recover the original key. + * + * @param wrappedKey + * @param additionalAssociatedData Optional additional data which was provided during wrapping. + * Both null and arrays of length 0 are treated identically. Not all keys will + * support this parameter. + * @param algorithm the transformation to be used when unwrapping the key + * @return the unwrapped key + * @throws UnsupportedOperationException if wrapping is not supported or if + * additionalAssociatedData is provided, but not supported. + */ + public Key unwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType, + byte[] additionalAssociatedData, + String algorithm) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException; - /** - * Calculates and returns a signature for dataToSign. - * - * @param dataToSign - * @param algorithm - * @return the signature - * @throws UnsupportedOperationException if signing is not supported - */ - public byte[] sign(byte[] dataToSign, String algorithm) throws GeneralSecurityException; + /** + * Calculates and returns a signature for dataToSign. + * + * @param dataToSign + * @param algorithm + * @return the signature + * @throws UnsupportedOperationException if signing is not supported + */ + public byte[] sign(byte[] dataToSign, String algorithm) throws GeneralSecurityException; - /** - * Checks the provided signature for correctness. - * - * @param dataToSign - * @param signature - * @param algorithm - * @return true if and only if the signature matches the dataToSign. - * @throws UnsupportedOperationException if signature validation is not supported - */ - public boolean verify(byte[] dataToSign, byte[] signature, String algorithm); + /** + * Checks the provided signature for correctness. + * + * @param dataToSign + * @param signature + * @param algorithm + * @return true if and only if the signature matches the dataToSign. + * @throws UnsupportedOperationException if signature validation is not supported + */ + public boolean verify(byte[] dataToSign, byte[] signature, String algorithm); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java index fcf067a8..45f49096 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java @@ -14,25 +14,22 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB; - /** * Prevents the associated item (class or attribute) from being encrypted. * - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

+ *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * - * @author Greg Rubin + * @author Greg Rubin */ @DynamoDB @Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Retention(value = RetentionPolicy.RUNTIME) -public @interface DoNotEncrypt { - -} +public @interface DoNotEncrypt {} diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java index ee2be7ec..6988ed1f 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java @@ -14,25 +14,22 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB; - /** * Prevents the associated item from being encrypted or signed. * - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

- * - * @author Greg Rubin + *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model + * + * @author Greg Rubin */ @DynamoDB @Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Retention(value = RetentionPolicy.RUNTIME) -public @interface DoNotTouch { - -} +public @interface DoNotTouch {} diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 678b0e40..5aa5a718 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -14,6 +14,14 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; +import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller; +import com.amazonaws.services.dynamodbv2.datamodeling.internal.ByteBufferInputStream; +import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -33,581 +41,582 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; - import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; -import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; -import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller; -import com.amazonaws.services.dynamodbv2.datamodeling.internal.ByteBufferInputStream; -import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; - /** - * The low-level API used by {@link AttributeEncryptor} to perform crypto - * operations on the record attributes. + * The low-level API used by {@link AttributeEncryptor} to perform crypto operations on the record + * attributes. * - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

+ *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * - * @author Greg Rubin + * @author Greg Rubin */ public class DynamoDBEncryptor { - private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA"; - private static final String DEFAULT_METADATA_FIELD = "*amzn-ddb-map-desc*"; - private static final String DEFAULT_SIGNATURE_FIELD = "*amzn-ddb-map-sig*"; - private static final String DEFAULT_DESCRIPTION_BASE = "amzn-ddb-map-"; // Same as the Mapper - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final String SYMMETRIC_ENCRYPTION_MODE = "/CBC/PKCS5Padding"; - private static final ConcurrentHashMap BLOCK_SIZE_CACHE = new ConcurrentHashMap<>(); - private static final Function BLOCK_SIZE_CALCULATOR = (transformation) -> { + private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA"; + private static final String DEFAULT_METADATA_FIELD = "*amzn-ddb-map-desc*"; + private static final String DEFAULT_SIGNATURE_FIELD = "*amzn-ddb-map-sig*"; + private static final String DEFAULT_DESCRIPTION_BASE = "amzn-ddb-map-"; // Same as the Mapper + private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final String SYMMETRIC_ENCRYPTION_MODE = "/CBC/PKCS5Padding"; + private static final ConcurrentHashMap BLOCK_SIZE_CACHE = + new ConcurrentHashMap<>(); + private static final Function BLOCK_SIZE_CALCULATOR = + (transformation) -> { try { - final Cipher c = Cipher.getInstance(transformation); - return c.getBlockSize(); + final Cipher c = Cipher.getInstance(transformation); + return c.getBlockSize(); } catch (final GeneralSecurityException ex) { - throw new IllegalArgumentException("Algorithm does not exist", ex); + throw new IllegalArgumentException("Algorithm does not exist", ex); } - }; - - private static final int CURRENT_VERSION = 0; - - private String signatureFieldName = DEFAULT_SIGNATURE_FIELD; - private String materialDescriptionFieldName = DEFAULT_METADATA_FIELD; - - private EncryptionMaterialsProvider encryptionMaterialsProvider; - private final String descriptionBase; - private final String symmetricEncryptionModeHeader; - private final String signingAlgorithmHeader; - - public static final String DEFAULT_SIGNING_ALGORITHM_HEADER = DEFAULT_DESCRIPTION_BASE + "signingAlg"; - private Function encryptionContextOverrideOperator; - - protected DynamoDBEncryptor(EncryptionMaterialsProvider provider, String descriptionBase) { - this.encryptionMaterialsProvider = provider; - this.descriptionBase = descriptionBase; - symmetricEncryptionModeHeader = this.descriptionBase + "sym-mode"; - signingAlgorithmHeader = this.descriptionBase + "signingAlg"; - } - - public static DynamoDBEncryptor getInstance(EncryptionMaterialsProvider provider, String descriptionbase) { - return new DynamoDBEncryptor(provider, descriptionbase); - } - - public static DynamoDBEncryptor getInstance(EncryptionMaterialsProvider provider) { - return getInstance(provider, DEFAULT_DESCRIPTION_BASE); - } - - /** - * Returns a decrypted version of the provided DynamoDb record. The signature is verified across - * all provided fields. All fields (except those listed in doNotEncrypt are - * decrypted. - * - * @param itemAttributes - * the DynamoDbRecord - * @param context - * additional information used to successfully select the encryption materials and - * decrypt the data. This should include (at least) the tableName and the - * materialDescription. - * @param doNotDecrypt - * those fields which should not be encrypted - * @return a plaintext version of the DynamoDb record - * @throws SignatureException - * if the signature is invalid or cannot be verified - * @throws GeneralSecurityException - */ - public Map decryptAllFieldsExcept(Map itemAttributes, - EncryptionContext context, String... doNotDecrypt) throws GeneralSecurityException { - return decryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotDecrypt)); - } - - /** - * @see #decryptAllFieldsExcept(Map, EncryptionContext, String...) - */ - public Map decryptAllFieldsExcept( - Map itemAttributes, - EncryptionContext context, Collection doNotDecrypt) - throws GeneralSecurityException { - Map> attributeFlags = allDecryptionFlagsExcept( - itemAttributes, doNotDecrypt); - return decryptRecord(itemAttributes, attributeFlags, context); + }; + + private static final int CURRENT_VERSION = 0; + + private String signatureFieldName = DEFAULT_SIGNATURE_FIELD; + private String materialDescriptionFieldName = DEFAULT_METADATA_FIELD; + + private EncryptionMaterialsProvider encryptionMaterialsProvider; + private final String descriptionBase; + private final String symmetricEncryptionModeHeader; + private final String signingAlgorithmHeader; + + public static final String DEFAULT_SIGNING_ALGORITHM_HEADER = + DEFAULT_DESCRIPTION_BASE + "signingAlg"; + private Function encryptionContextOverrideOperator; + + protected DynamoDBEncryptor(EncryptionMaterialsProvider provider, String descriptionBase) { + this.encryptionMaterialsProvider = provider; + this.descriptionBase = descriptionBase; + symmetricEncryptionModeHeader = this.descriptionBase + "sym-mode"; + signingAlgorithmHeader = this.descriptionBase + "signingAlg"; + } + + public static DynamoDBEncryptor getInstance( + EncryptionMaterialsProvider provider, String descriptionbase) { + return new DynamoDBEncryptor(provider, descriptionbase); + } + + public static DynamoDBEncryptor getInstance(EncryptionMaterialsProvider provider) { + return getInstance(provider, DEFAULT_DESCRIPTION_BASE); + } + + /** + * Returns a decrypted version of the provided DynamoDb record. The signature is verified across + * all provided fields. All fields (except those listed in doNotEncrypt are + * decrypted. + * + * @param itemAttributes the DynamoDbRecord + * @param context additional information used to successfully select the encryption materials and + * decrypt the data. This should include (at least) the tableName and the materialDescription. + * @param doNotDecrypt those fields which should not be encrypted + * @return a plaintext version of the DynamoDb record + * @throws SignatureException if the signature is invalid or cannot be verified + * @throws GeneralSecurityException + */ + public Map decryptAllFieldsExcept( + Map itemAttributes, EncryptionContext context, String... doNotDecrypt) + throws GeneralSecurityException { + return decryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotDecrypt)); + } + + /** @see #decryptAllFieldsExcept(Map, EncryptionContext, String...) */ + public Map decryptAllFieldsExcept( + Map itemAttributes, + EncryptionContext context, + Collection doNotDecrypt) + throws GeneralSecurityException { + Map> attributeFlags = + allDecryptionFlagsExcept(itemAttributes, doNotDecrypt); + return decryptRecord(itemAttributes, attributeFlags, context); + } + + /** + * Returns the decryption flags for all item attributes except for those explicitly specified to + * be excluded. + * + * @param doNotDecrypt fields to be excluded + */ + public Map> allDecryptionFlagsExcept( + Map itemAttributes, String... doNotDecrypt) { + return allDecryptionFlagsExcept(itemAttributes, Arrays.asList(doNotDecrypt)); + } + + /** + * Returns the decryption flags for all item attributes except for those explicitly specified to + * be excluded. + * + * @param doNotDecrypt fields to be excluded + */ + public Map> allDecryptionFlagsExcept( + Map itemAttributes, Collection doNotDecrypt) { + Map> attributeFlags = new HashMap>(); + + for (String fieldName : doNotDecrypt) { + attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN)); } - /** - * Returns the decryption flags for all item attributes except for those - * explicitly specified to be excluded. - * @param doNotDecrypt fields to be excluded - */ - public Map> allDecryptionFlagsExcept( - Map itemAttributes, - String ... doNotDecrypt) { - return allDecryptionFlagsExcept(itemAttributes, Arrays.asList(doNotDecrypt)); + for (String fieldName : itemAttributes.keySet()) { + if (!attributeFlags.containsKey(fieldName) + && !fieldName.equals(getMaterialDescriptionFieldName()) + && !fieldName.equals(getSignatureFieldName())) { + attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN)); + } } - - /** - * Returns the decryption flags for all item attributes except for those - * explicitly specified to be excluded. - * @param doNotDecrypt fields to be excluded - */ - public Map> allDecryptionFlagsExcept( - Map itemAttributes, - Collection doNotDecrypt) { - Map> attributeFlags = new HashMap>(); - - for (String fieldName : doNotDecrypt) { - attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN)); - } - - for (String fieldName : itemAttributes.keySet()) { - if (!attributeFlags.containsKey(fieldName) && - !fieldName.equals(getMaterialDescriptionFieldName()) && - !fieldName.equals(getSignatureFieldName())) { - attributeFlags.put(fieldName, - EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN)); - } - } - return attributeFlags; + return attributeFlags; + } + + /** + * Returns an encrypted version of the provided DynamoDb record. All fields are signed. All fields + * (except those listed in doNotEncrypt) are encrypted. + * + * @param itemAttributes a DynamoDb Record + * @param context additional information used to successfully select the encryption materials and + * encrypt the data. This should include (at least) the tableName. + * @param doNotEncrypt those fields which should not be encrypted + * @return a ciphertext version of the DynamoDb record + * @throws GeneralSecurityException + */ + public Map encryptAllFieldsExcept( + Map itemAttributes, EncryptionContext context, String... doNotEncrypt) + throws GeneralSecurityException { + + return encryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotEncrypt)); + } + + public Map encryptAllFieldsExcept( + Map itemAttributes, + EncryptionContext context, + Collection doNotEncrypt) + throws GeneralSecurityException { + Map> attributeFlags = + allEncryptionFlagsExcept(itemAttributes, doNotEncrypt); + return encryptRecord(itemAttributes, attributeFlags, context); + } + + /** + * Returns the encryption flags for all item attributes except for those explicitly specified to + * be excluded. + * + * @param doNotEncrypt fields to be excluded + */ + public Map> allEncryptionFlagsExcept( + Map itemAttributes, String... doNotEncrypt) { + return allEncryptionFlagsExcept(itemAttributes, Arrays.asList(doNotEncrypt)); + } + + /** + * Returns the encryption flags for all item attributes except for those explicitly specified to + * be excluded. + * + * @param doNotEncrypt fields to be excluded + */ + public Map> allEncryptionFlagsExcept( + Map itemAttributes, Collection doNotEncrypt) { + Map> attributeFlags = new HashMap>(); + for (String fieldName : doNotEncrypt) { + attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN)); } - - /** - * Returns an encrypted version of the provided DynamoDb record. All fields are signed. All fields - * (except those listed in doNotEncrypt) are encrypted. - * @param itemAttributes a DynamoDb Record - * @param context - * additional information used to successfully select the encryption materials and - * encrypt the data. This should include (at least) the tableName. - * @param doNotEncrypt those fields which should not be encrypted - * @return a ciphertext version of the DynamoDb record - * @throws GeneralSecurityException - */ - public Map encryptAllFieldsExcept(Map itemAttributes, - EncryptionContext context, String... doNotEncrypt) throws GeneralSecurityException { - - return encryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotEncrypt)); + + for (String fieldName : itemAttributes.keySet()) { + if (!attributeFlags.containsKey(fieldName)) { + attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN)); + } } - - public Map encryptAllFieldsExcept( - Map itemAttributes, - EncryptionContext context, - Collection doNotEncrypt) - throws GeneralSecurityException { - Map> attributeFlags = allEncryptionFlagsExcept( - itemAttributes, doNotEncrypt); - return encryptRecord(itemAttributes, attributeFlags, context); + return attributeFlags; + } + + public Map decryptRecord( + Map itemAttributes, + Map> attributeFlags, + EncryptionContext context) + throws GeneralSecurityException { + if (attributeFlags.isEmpty()) { + return itemAttributes; } + // Copy to avoid changing anyone elses objects + itemAttributes = new HashMap(itemAttributes); - /** - * Returns the encryption flags for all item attributes except for those - * explicitly specified to be excluded. - * @param doNotEncrypt fields to be excluded - */ - public Map> allEncryptionFlagsExcept( - Map itemAttributes, - String ...doNotEncrypt) { - return allEncryptionFlagsExcept(itemAttributes, Arrays.asList(doNotEncrypt)); - } + Map materialDescription = Collections.emptyMap(); + DecryptionMaterials materials; + SecretKey decryptionKey; - /** - * Returns the encryption flags for all item attributes except for those - * explicitly specified to be excluded. - * @param doNotEncrypt fields to be excluded - */ - public Map> allEncryptionFlagsExcept( - Map itemAttributes, - Collection doNotEncrypt) { - Map> attributeFlags = - new HashMap>(); - for (String fieldName : doNotEncrypt) { - attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN)); - } + DynamoDBSigner signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng()); - for (String fieldName : itemAttributes.keySet()) { - if (!attributeFlags.containsKey(fieldName)) { - attributeFlags.put(fieldName, - EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN)); - } - } - return attributeFlags; + if (itemAttributes.containsKey(materialDescriptionFieldName)) { + materialDescription = unmarshallDescription(itemAttributes.get(materialDescriptionFieldName)); } - - public Map decryptRecord( - Map itemAttributes, - Map> attributeFlags, - EncryptionContext context) throws GeneralSecurityException { - if (attributeFlags.isEmpty()) { - return itemAttributes; - } - // Copy to avoid changing anyone elses objects - itemAttributes = new HashMap(itemAttributes); - - Map materialDescription = Collections.emptyMap(); - DecryptionMaterials materials; - SecretKey decryptionKey; - - DynamoDBSigner signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng()); - - if (itemAttributes.containsKey(materialDescriptionFieldName)) { - materialDescription = unmarshallDescription(itemAttributes.get(materialDescriptionFieldName)); - } - // Copy the material description and attribute values into the context - context = new EncryptionContext.Builder(context) + // Copy the material description and attribute values into the context + context = + new EncryptionContext.Builder(context) .withMaterialDescription(materialDescription) .withAttributeValues(itemAttributes) .build(); - Function encryptionContextOverrideOperator = getEncryptionContextOverrideOperator(); - if (encryptionContextOverrideOperator != null) { - context = encryptionContextOverrideOperator.apply(context); - } - - materials = encryptionMaterialsProvider.getDecryptionMaterials(context); - decryptionKey = materials.getDecryptionKey(); - if (materialDescription.containsKey(signingAlgorithmHeader)) { - String signingAlg = materialDescription.get(signingAlgorithmHeader); - signer = DynamoDBSigner.getInstance(signingAlg, Utils.getRng()); - } - - ByteBuffer signature; - if (!itemAttributes.containsKey(signatureFieldName) || itemAttributes.get(signatureFieldName).getB() == null) { - signature = ByteBuffer.allocate(0); - } else { - signature = itemAttributes.get(signatureFieldName).getB().asReadOnlyBuffer(); - } - itemAttributes.remove(signatureFieldName); - - String associatedData = "TABLE>" + context.getTableName() + " encryptionContextOverrideOperator = + getEncryptionContextOverrideOperator(); + if (encryptionContextOverrideOperator != null) { + context = encryptionContextOverrideOperator.apply(context); } - /** - * Returns the encrypted (and signed) record, which is a map of item - * attributes. There is no side effect on the input parameters upon calling - * this method. - * - * @param itemAttributes - * the input record - * @param attributeFlags - * the corresponding encryption flags - * @param context - * encryption context - * @return a new instance of item attributes encrypted as necessary - * @throws GeneralSecurityException - * if failed to encrypt the record - */ - public Map encryptRecord( - Map itemAttributes, - Map> attributeFlags, - EncryptionContext context) throws GeneralSecurityException { - if (attributeFlags.isEmpty()) { - return itemAttributes; - } - // Copy to avoid changing anyone elses objects - itemAttributes = new HashMap(itemAttributes); - - // Copy the attribute values into the context - context = new EncryptionContext.Builder(context) - .withAttributeValues(itemAttributes) - .build(); - - Function encryptionContextOverrideOperator = - getEncryptionContextOverrideOperator(); - if (encryptionContextOverrideOperator != null) { - context = encryptionContextOverrideOperator.apply(context); - } - - EncryptionMaterials materials = encryptionMaterialsProvider.getEncryptionMaterials(context); - // We need to copy this because we modify it to record other encryption details - Map materialDescription = new HashMap( - materials.getMaterialDescription()); - SecretKey encryptionKey = materials.getEncryptionKey(); - - actualEncryption(itemAttributes, attributeFlags, materialDescription, encryptionKey); - - // The description must be stored after encryption because its data - // is necessary for proper decryption. - final String signingAlgo = materialDescription.get(signingAlgorithmHeader); - DynamoDBSigner signer; - if (signingAlgo != null) { - signer = DynamoDBSigner.getInstance(signingAlgo, Utils.getRng()); - } else { - signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng()); - } - - if (materials.getSigningKey() instanceof PrivateKey ) { - materialDescription.put(signingAlgorithmHeader, signer.getSigningAlgorithm()); - } - if (!materialDescription.isEmpty()) { - itemAttributes.put(materialDescriptionFieldName, marshallDescription(materialDescription)); - } - - String associatedData = "TABLE>" + context.getTableName() + " itemAttributes, - Map> attributeFlags, SecretKey encryptionKey, - Map materialDescription) throws GeneralSecurityException { - final String encryptionMode = encryptionKey != null ? encryptionKey.getAlgorithm() + - materialDescription.get(symmetricEncryptionModeHeader) : null; - Cipher cipher = null; - int blockSize = -1; - - for (Map.Entry entry: itemAttributes.entrySet()) { - Set flags = attributeFlags.get(entry.getKey()); - if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) { - if (!flags.contains(EncryptionFlags.SIGN)) { - throw new IllegalArgumentException("All encrypted fields must be signed. Bad field: " + entry.getKey()); - } - ByteBuffer plainText; - ByteBuffer cipherText = entry.getValue().getB().asReadOnlyBuffer(); - cipherText.rewind(); - if (encryptionKey instanceof DelegatedKey) { - plainText = ByteBuffer.wrap(((DelegatedKey)encryptionKey).decrypt(toByteArray(cipherText), null, encryptionMode)); - } else { - if (cipher == null) { - blockSize = getBlockSize(encryptionMode); - cipher = Cipher.getInstance(encryptionMode); - } - byte[] iv = new byte[blockSize]; - cipherText.get(iv); - cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv), Utils.getRng()); - plainText = ByteBuffer.allocate(cipher.getOutputSize(cipherText.remaining())); - cipher.doFinal(cipherText, plainText); - plainText.rewind(); - } - entry.setValue(AttributeValueMarshaller.unmarshall(plainText)); - } - } + itemAttributes.remove(signatureFieldName); + + String associatedData = "TABLE>" + context.getTableName() + " encryptRecord( + Map itemAttributes, + Map> attributeFlags, + EncryptionContext context) + throws GeneralSecurityException { + if (attributeFlags.isEmpty()) { + return itemAttributes; } + // Copy to avoid changing anyone elses objects + itemAttributes = new HashMap(itemAttributes); - protected static int getBlockSize(final String encryptionMode) { - return BLOCK_SIZE_CACHE.computeIfAbsent(encryptionMode, BLOCK_SIZE_CALCULATOR); - } + // Copy the attribute values into the context + context = new EncryptionContext.Builder(context).withAttributeValues(itemAttributes).build(); - /** - * This method has the side effect of replacing the plaintext - * attribute-values of "itemAttributes" with ciphertext attribute-values - * (which are always in the form of ByteBuffer) as per the corresponding - * attribute flags. - */ - private void actualEncryption(Map itemAttributes, - Map> attributeFlags, - Map materialDescription, - SecretKey encryptionKey) throws GeneralSecurityException { - String encryptionMode = null; - if (encryptionKey != null) { - materialDescription.put(this.symmetricEncryptionModeHeader, - SYMMETRIC_ENCRYPTION_MODE); - encryptionMode = encryptionKey.getAlgorithm() + SYMMETRIC_ENCRYPTION_MODE; - } - Cipher cipher = null; - int blockSize = -1; - - for (Map.Entry entry: itemAttributes.entrySet()) { - Set flags = attributeFlags.get(entry.getKey()); - if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) { - if (!flags.contains(EncryptionFlags.SIGN)) { - throw new IllegalArgumentException("All encrypted fields must be signed. Bad field: " + entry.getKey()); - } - ByteBuffer plainText = AttributeValueMarshaller.marshall(entry.getValue()); - plainText.rewind(); - ByteBuffer cipherText; - if (encryptionKey instanceof DelegatedKey) { - DelegatedKey dk = (DelegatedKey) encryptionKey; - cipherText = ByteBuffer.wrap( - dk.encrypt(toByteArray(plainText), null, encryptionMode)); - } else { - if (cipher == null) { - blockSize = getBlockSize(encryptionMode); - cipher = Cipher.getInstance(encryptionMode); - } - // Encryption format: - // Note a unique iv is generated per attribute - cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, Utils.getRng()); - cipherText = ByteBuffer.allocate(blockSize + cipher.getOutputSize(plainText.remaining())); - cipherText.position(blockSize); - cipher.doFinal(plainText, cipherText); - cipherText.flip(); - final byte[] iv = cipher.getIV(); - if (iv.length != blockSize) { - throw new IllegalStateException(String.format("Generated IV length (%d) not equal to block size (%d)", - iv.length, blockSize)); - } - cipherText.put(iv); - cipherText.rewind(); - } - // Replace the plaintext attribute value with the encrypted content - entry.setValue(new AttributeValue().withB(cipherText)); - } - } - } - - /** - * Get the name of the DynamoDB field used to store the signature. - * Defaults to {@link #DEFAULT_SIGNATURE_FIELD}. - * - * @return the name of the DynamoDB field used to store the signature - */ - public String getSignatureFieldName() { - return signatureFieldName; + Function encryptionContextOverrideOperator = + getEncryptionContextOverrideOperator(); + if (encryptionContextOverrideOperator != null) { + context = encryptionContextOverrideOperator.apply(context); } - /** - * Set the name of the DynamoDB field used to store the signature. - * - * @param signatureFieldName - */ - public void setSignatureFieldName(final String signatureFieldName) { - this.signatureFieldName = signatureFieldName; + EncryptionMaterials materials = encryptionMaterialsProvider.getEncryptionMaterials(context); + // We need to copy this because we modify it to record other encryption details + Map materialDescription = + new HashMap(materials.getMaterialDescription()); + SecretKey encryptionKey = materials.getEncryptionKey(); + + actualEncryption(itemAttributes, attributeFlags, materialDescription, encryptionKey); + + // The description must be stored after encryption because its data + // is necessary for proper decryption. + final String signingAlgo = materialDescription.get(signingAlgorithmHeader); + DynamoDBSigner signer; + if (signingAlgo != null) { + signer = DynamoDBSigner.getInstance(signingAlgo, Utils.getRng()); + } else { + signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng()); } - /** - * Get the name of the DynamoDB field used to store metadata used by the - * DynamoDBEncryptedMapper. Defaults to {@link #DEFAULT_METADATA_FIELD}. - * - * @return the name of the DynamoDB field used to store metadata used by the - * DynamoDBEncryptedMapper - */ - public String getMaterialDescriptionFieldName() { - return materialDescriptionFieldName; + if (materials.getSigningKey() instanceof PrivateKey) { + materialDescription.put(signingAlgorithmHeader, signer.getSigningAlgorithm()); } - - /** - * Set the name of the DynamoDB field used to store metadata used by the - * DynamoDBEncryptedMapper - * - * @param materialDescriptionFieldName - */ - public void setMaterialDescriptionFieldName(final String materialDescriptionFieldName) { - this.materialDescriptionFieldName = materialDescriptionFieldName; + if (!materialDescription.isEmpty()) { + itemAttributes.put(materialDescriptionFieldName, marshallDescription(materialDescription)); } - - /** - * Marshalls the description into a ByteBuffer by outputting - * each key (modified UTF-8) followed by its value (also in modified UTF-8). - * - * @param description - * @return the description encoded as an AttributeValue with a ByteBuffer value - * @see java.io.DataOutput#writeUTF(String) - */ - protected static AttributeValue marshallDescription(Map description) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(bos); - out.writeInt(CURRENT_VERSION); - for (Map.Entry entry : description.entrySet()) { - byte[] bytes = entry.getKey().getBytes(UTF8); - out.writeInt(bytes.length); - out.write(bytes); - bytes = entry.getValue().getBytes(UTF8); - out.writeInt(bytes.length); - out.write(bytes); - } - out.close(); - AttributeValue result = new AttributeValue(); - result.setB(ByteBuffer.wrap(bos.toByteArray())); - return result; - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); + + String associatedData = "TABLE>" + context.getTableName() + " itemAttributes, + Map> attributeFlags, + SecretKey encryptionKey, + Map materialDescription) + throws GeneralSecurityException { + final String encryptionMode = + encryptionKey != null + ? encryptionKey.getAlgorithm() + materialDescription.get(symmetricEncryptionModeHeader) + : null; + Cipher cipher = null; + int blockSize = -1; + + for (Map.Entry entry : itemAttributes.entrySet()) { + Set flags = attributeFlags.get(entry.getKey()); + if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) { + if (!flags.contains(EncryptionFlags.SIGN)) { + throw new IllegalArgumentException( + "All encrypted fields must be signed. Bad field: " + entry.getKey()); } + ByteBuffer plainText; + ByteBuffer cipherText = entry.getValue().getB().asReadOnlyBuffer(); + cipherText.rewind(); + if (encryptionKey instanceof DelegatedKey) { + plainText = + ByteBuffer.wrap( + ((DelegatedKey) encryptionKey) + .decrypt(toByteArray(cipherText), null, encryptionMode)); + } else { + if (cipher == null) { + blockSize = getBlockSize(encryptionMode); + cipher = Cipher.getInstance(encryptionMode); + } + byte[] iv = new byte[blockSize]; + cipherText.get(iv); + cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv), Utils.getRng()); + plainText = ByteBuffer.allocate(cipher.getOutputSize(cipherText.remaining())); + cipher.doFinal(cipherText, plainText); + plainText.rewind(); + } + entry.setValue(AttributeValueMarshaller.unmarshall(plainText)); + } } - - public String getSigningAlgorithmHeader() { - return signingAlgorithmHeader; + } + + protected static int getBlockSize(final String encryptionMode) { + return BLOCK_SIZE_CACHE.computeIfAbsent(encryptionMode, BLOCK_SIZE_CALCULATOR); + } + + /** + * This method has the side effect of replacing the plaintext attribute-values of "itemAttributes" + * with ciphertext attribute-values (which are always in the form of ByteBuffer) as per the + * corresponding attribute flags. + */ + private void actualEncryption( + Map itemAttributes, + Map> attributeFlags, + Map materialDescription, + SecretKey encryptionKey) + throws GeneralSecurityException { + String encryptionMode = null; + if (encryptionKey != null) { + materialDescription.put(this.symmetricEncryptionModeHeader, SYMMETRIC_ENCRYPTION_MODE); + encryptionMode = encryptionKey.getAlgorithm() + SYMMETRIC_ENCRYPTION_MODE; } - /** - * @see #marshallDescription(Map) - */ - protected static Map unmarshallDescription(AttributeValue attributeValue) { - attributeValue.getB().mark(); - try (DataInputStream in = new DataInputStream( - new ByteBufferInputStream(attributeValue.getB())) ) { - Map result = new HashMap(); - int version = in.readInt(); - if (version != CURRENT_VERSION) { - throw new IllegalArgumentException("Unsupported description version"); - } - - String key, value; - int keyLength, valueLength; - try { - while(in.available() > 0) { - keyLength = in.readInt(); - byte[] bytes = new byte[keyLength]; - if (in.read(bytes) != keyLength) { - throw new IllegalArgumentException("Malformed description"); - } - key = new String(bytes, UTF8); - valueLength = in.readInt(); - bytes = new byte[valueLength]; - if (in.read(bytes) != valueLength) { - throw new IllegalArgumentException("Malformed description"); - } - value = new String(bytes, UTF8); - result.put(key, value); - } - } catch (EOFException eof) { - throw new IllegalArgumentException("Malformed description", eof); - } - return result; - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } finally { - attributeValue.getB().reset(); + Cipher cipher = null; + int blockSize = -1; + + for (Map.Entry entry : itemAttributes.entrySet()) { + Set flags = attributeFlags.get(entry.getKey()); + if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) { + if (!flags.contains(EncryptionFlags.SIGN)) { + throw new IllegalArgumentException( + "All encrypted fields must be signed. Bad field: " + entry.getKey()); } + ByteBuffer plainText = AttributeValueMarshaller.marshall(entry.getValue()); + plainText.rewind(); + ByteBuffer cipherText; + if (encryptionKey instanceof DelegatedKey) { + DelegatedKey dk = (DelegatedKey) encryptionKey; + cipherText = ByteBuffer.wrap(dk.encrypt(toByteArray(plainText), null, encryptionMode)); + } else { + if (cipher == null) { + blockSize = getBlockSize(encryptionMode); + cipher = Cipher.getInstance(encryptionMode); + } + // Encryption format: + // Note a unique iv is generated per attribute + cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, Utils.getRng()); + cipherText = ByteBuffer.allocate(blockSize + cipher.getOutputSize(plainText.remaining())); + cipherText.position(blockSize); + cipher.doFinal(plainText, cipherText); + cipherText.flip(); + final byte[] iv = cipher.getIV(); + if (iv.length != blockSize) { + throw new IllegalStateException( + String.format( + "Generated IV length (%d) not equal to block size (%d)", iv.length, blockSize)); + } + cipherText.put(iv); + cipherText.rewind(); + } + // Replace the plaintext attribute value with the encrypted content + entry.setValue(new AttributeValue().withB(cipherText)); + } } - - /** - * @param encryptionContextOverrideOperator the nullable operator which will be used to override - * the EncryptionContext. - * @see com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators - */ - public final void setEncryptionContextOverrideOperator( - Function encryptionContextOverrideOperator) { - this.encryptionContextOverrideOperator = encryptionContextOverrideOperator; - } - - /** - * @return the operator used to override the EncryptionContext - * @see #setEncryptionContextOverrideOperator(Function) - */ - public final Function getEncryptionContextOverrideOperator() { - return encryptionContextOverrideOperator; + } + + /** + * Get the name of the DynamoDB field used to store the signature. Defaults to {@link + * #DEFAULT_SIGNATURE_FIELD}. + * + * @return the name of the DynamoDB field used to store the signature + */ + public String getSignatureFieldName() { + return signatureFieldName; + } + + /** + * Set the name of the DynamoDB field used to store the signature. + * + * @param signatureFieldName + */ + public void setSignatureFieldName(final String signatureFieldName) { + this.signatureFieldName = signatureFieldName; + } + + /** + * Get the name of the DynamoDB field used to store metadata used by the DynamoDBEncryptedMapper. + * Defaults to {@link #DEFAULT_METADATA_FIELD}. + * + * @return the name of the DynamoDB field used to store metadata used by the + * DynamoDBEncryptedMapper + */ + public String getMaterialDescriptionFieldName() { + return materialDescriptionFieldName; + } + + /** + * Set the name of the DynamoDB field used to store metadata used by the DynamoDBEncryptedMapper + * + * @param materialDescriptionFieldName + */ + public void setMaterialDescriptionFieldName(final String materialDescriptionFieldName) { + this.materialDescriptionFieldName = materialDescriptionFieldName; + } + + /** + * Marshalls the description into a ByteBuffer by outputting each key (modified + * UTF-8) followed by its value (also in modified UTF-8). + * + * @param description + * @return the description encoded as an AttributeValue with a ByteBuffer value + * @see java.io.DataOutput#writeUTF(String) + */ + protected static AttributeValue marshallDescription(Map description) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos); + out.writeInt(CURRENT_VERSION); + for (Map.Entry entry : description.entrySet()) { + byte[] bytes = entry.getKey().getBytes(UTF8); + out.writeInt(bytes.length); + out.write(bytes); + bytes = entry.getValue().getBytes(UTF8); + out.writeInt(bytes.length); + out.write(bytes); + } + out.close(); + AttributeValue result = new AttributeValue(); + result.setB(ByteBuffer.wrap(bos.toByteArray())); + return result; + } catch (IOException ex) { + // Due to the objects in use, an IOException is not possible. + throw new RuntimeException("Unexpected exception", ex); } - - private static byte[] toByteArray(ByteBuffer buffer) { - buffer = buffer.duplicate(); - // We can only return the array directly if: - // 1. The ByteBuffer exposes an array - // 2. The ByteBuffer starts at the beginning of the array - // 3. The ByteBuffer uses the entire array - if (buffer.hasArray() && buffer.arrayOffset() == 0) { - byte[] result = buffer.array(); - if (buffer.remaining() == result.length) { - return result; - } + } + + public String getSigningAlgorithmHeader() { + return signingAlgorithmHeader; + } + /** @see #marshallDescription(Map) */ + protected static Map unmarshallDescription(AttributeValue attributeValue) { + attributeValue.getB().mark(); + try (DataInputStream in = + new DataInputStream(new ByteBufferInputStream(attributeValue.getB()))) { + Map result = new HashMap(); + int version = in.readInt(); + if (version != CURRENT_VERSION) { + throw new IllegalArgumentException("Unsupported description version"); + } + + String key, value; + int keyLength, valueLength; + try { + while (in.available() > 0) { + keyLength = in.readInt(); + byte[] bytes = new byte[keyLength]; + if (in.read(bytes) != keyLength) { + throw new IllegalArgumentException("Malformed description"); + } + key = new String(bytes, UTF8); + valueLength = in.readInt(); + bytes = new byte[valueLength]; + if (in.read(bytes) != valueLength) { + throw new IllegalArgumentException("Malformed description"); + } + value = new String(bytes, UTF8); + result.put(key, value); } - - byte[] result = new byte[buffer.remaining()]; - buffer.get(result); + } catch (EOFException eof) { + throw new IllegalArgumentException("Malformed description", eof); + } + return result; + } catch (IOException ex) { + // Due to the objects in use, an IOException is not possible. + throw new RuntimeException("Unexpected exception", ex); + } finally { + attributeValue.getB().reset(); + } + } + + /** + * @param encryptionContextOverrideOperator the nullable operator which will be used to override + * the EncryptionContext. + * @see com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators + */ + public final void setEncryptionContextOverrideOperator( + Function encryptionContextOverrideOperator) { + this.encryptionContextOverrideOperator = encryptionContextOverrideOperator; + } + + /** + * @return the operator used to override the EncryptionContext + * @see #setEncryptionContextOverrideOperator(Function) + */ + public final Function + getEncryptionContextOverrideOperator() { + return encryptionContextOverrideOperator; + } + + private static byte[] toByteArray(ByteBuffer buffer) { + buffer = buffer.duplicate(); + // We can only return the array directly if: + // 1. The ByteBuffer exposes an array + // 2. The ByteBuffer starts at the beginning of the array + // 3. The ByteBuffer uses the entire array + if (buffer.hasArray() && buffer.arrayOffset() == 0) { + byte[] result = buffer.array(); + if (buffer.remaining() == result.length) { return result; + } } + + byte[] result = new byte[buffer.remaining()]; + buffer.get(result); + return result; + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java index f4a13905..3a4aa14d 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java @@ -14,6 +14,9 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller; +import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -33,221 +36,227 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller; -import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; - /** - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * - * @author Greg Rubin + * @author Greg Rubin */ // NOTE: This class must remain thread-safe. class DynamoDBSigner { - private static final ConcurrentHashMap cache = - new ConcurrentHashMap(); - - protected static final Charset UTF8 = Charset.forName("UTF-8"); - private final SecureRandom rnd; - private final SecretKey hmacComparisonKey; - private final String signingAlgorithm; - - /** - * @param signingAlgorithm - * is the algorithm used for asymmetric signing (ex: - * SHA256withRSA). This is ignored for symmetric HMACs as that - * algorithm is fully specified by the key. - */ - static DynamoDBSigner getInstance(String signingAlgorithm, SecureRandom rnd) { - DynamoDBSigner result = cache.get(signingAlgorithm); - if (result == null) { - result = new DynamoDBSigner(signingAlgorithm, rnd); - cache.putIfAbsent(signingAlgorithm, result); - } - return result; - } + private static final ConcurrentHashMap cache = + new ConcurrentHashMap(); - /** - * @param signingAlgorithm - * is the algorithm used for asymmetric signing (ex: - * SHA256withRSA). This is ignored for symmetric HMACs as that - * algorithm is fully specified by the key. - */ - private DynamoDBSigner(String signingAlgorithm, SecureRandom rnd) { - if (rnd == null) { - rnd = Utils.getRng(); - } - this.rnd = rnd; - this.signingAlgorithm = signingAlgorithm; - // Shorter than the output of SHA256 to avoid weak keys. - // http://cs.nyu.edu/~dodis/ps/h-of-h.pdf - // http://link.springer.com/chapter/10.1007%2F978-3-642-32009-5_21 - byte[] tmpKey = new byte[31]; - rnd.nextBytes(tmpKey); - hmacComparisonKey = new SecretKeySpec(tmpKey, "HmacSHA256"); - } + protected static final Charset UTF8 = Charset.forName("UTF-8"); + private final SecureRandom rnd; + private final SecretKey hmacComparisonKey; + private final String signingAlgorithm; - void verifySignature(Map itemAttributes, Map> attributeFlags, - byte[] associatedData, Key verificationKey, ByteBuffer signature) throws GeneralSecurityException { - if (verificationKey instanceof DelegatedKey) { - DelegatedKey dKey = (DelegatedKey)verificationKey; - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - if (!dKey.verify(stringToSign, toByteArray(signature), dKey.getAlgorithm())) { - throw new SignatureException("Bad signature"); - } - } else if (verificationKey instanceof SecretKey) { - byte[] calculatedSig = calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey)verificationKey); - if (!safeEquals(signature, calculatedSig)) { - throw new SignatureException("Bad signature"); - } - } else if (verificationKey instanceof PublicKey) { - PublicKey integrityKey = (PublicKey)verificationKey; - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - Signature sig = Signature.getInstance(getSigningAlgorithm()); - sig.initVerify(integrityKey); - sig.update(stringToSign); - if (!sig.verify(toByteArray(signature))) { - throw new SignatureException("Bad signature"); - } - } else { - throw new IllegalArgumentException("No integrity key provided"); - } + /** + * @param signingAlgorithm is the algorithm used for asymmetric signing (ex: SHA256withRSA). This + * is ignored for symmetric HMACs as that algorithm is fully specified by the key. + */ + static DynamoDBSigner getInstance(String signingAlgorithm, SecureRandom rnd) { + DynamoDBSigner result = cache.get(signingAlgorithm); + if (result == null) { + result = new DynamoDBSigner(signingAlgorithm, rnd); + cache.putIfAbsent(signingAlgorithm, result); } + return result; + } - static byte[] calculateStringToSign(Map itemAttributes, - Map> attributeFlags, byte[] associatedData) - throws NoSuchAlgorithmException { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - List attrNames = new ArrayList(itemAttributes.keySet()); - Collections.sort(attrNames); - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - if (associatedData != null) { - out.write(sha256.digest(associatedData)); - } else { - out.write(sha256.digest()); - } - sha256.reset(); - - for (String name : attrNames) { - Set set = attributeFlags.get(name); - if(set != null && set.contains(EncryptionFlags.SIGN)) { - AttributeValue tmp = itemAttributes.get(name); - out.write(sha256.digest(name.getBytes(UTF8))); - sha256.reset(); - if (set.contains(EncryptionFlags.ENCRYPT)) { - sha256.update("ENCRYPTED".getBytes(UTF8)); - } else { - sha256.update("PLAINTEXT".getBytes(UTF8)); - } - out.write(sha256.digest()); - - sha256.reset(); - - sha256.update(AttributeValueMarshaller.marshall(tmp)); - out.write(sha256.digest()); - sha256.reset(); - } - } - return out.toByteArray(); - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } + /** + * @param signingAlgorithm is the algorithm used for asymmetric signing (ex: SHA256withRSA). This + * is ignored for symmetric HMACs as that algorithm is fully specified by the key. + */ + private DynamoDBSigner(String signingAlgorithm, SecureRandom rnd) { + if (rnd == null) { + rnd = Utils.getRng(); } + this.rnd = rnd; + this.signingAlgorithm = signingAlgorithm; + // Shorter than the output of SHA256 to avoid weak keys. + // http://cs.nyu.edu/~dodis/ps/h-of-h.pdf + // http://link.springer.com/chapter/10.1007%2F978-3-642-32009-5_21 + byte[] tmpKey = new byte[31]; + rnd.nextBytes(tmpKey); + hmacComparisonKey = new SecretKeySpec(tmpKey, "HmacSHA256"); + } - /** - * The itemAttributes have already been encrypted, if necessary, before the - * signing. - */ - byte[] calculateSignature( - Map itemAttributes, - Map> attributeFlags, - byte[] associatedData, Key key) throws GeneralSecurityException { - if (key instanceof DelegatedKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key); - } else if (key instanceof SecretKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey) key); - } else if (key instanceof PrivateKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (PrivateKey) key); - } else { - throw new IllegalArgumentException("No integrity key provided"); - } + void verifySignature( + Map itemAttributes, + Map> attributeFlags, + byte[] associatedData, + Key verificationKey, + ByteBuffer signature) + throws GeneralSecurityException { + if (verificationKey instanceof DelegatedKey) { + DelegatedKey dKey = (DelegatedKey) verificationKey; + byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); + if (!dKey.verify(stringToSign, toByteArray(signature), dKey.getAlgorithm())) { + throw new SignatureException("Bad signature"); + } + } else if (verificationKey instanceof SecretKey) { + byte[] calculatedSig = + calculateSignature( + itemAttributes, attributeFlags, associatedData, (SecretKey) verificationKey); + if (!safeEquals(signature, calculatedSig)) { + throw new SignatureException("Bad signature"); + } + } else if (verificationKey instanceof PublicKey) { + PublicKey integrityKey = (PublicKey) verificationKey; + byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); + Signature sig = Signature.getInstance(getSigningAlgorithm()); + sig.initVerify(integrityKey); + sig.update(stringToSign); + if (!sig.verify(toByteArray(signature))) { + throw new SignatureException("Bad signature"); + } + } else { + throw new IllegalArgumentException("No integrity key provided"); } + } - byte[] calculateSignature(Map itemAttributes, - Map> attributeFlags, byte[] associatedData, - DelegatedKey key) throws GeneralSecurityException { - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - return key.sign(stringToSign, key.getAlgorithm()); - } + static byte[] calculateStringToSign( + Map itemAttributes, + Map> attributeFlags, + byte[] associatedData) + throws NoSuchAlgorithmException { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + List attrNames = new ArrayList(itemAttributes.keySet()); + Collections.sort(attrNames); + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + if (associatedData != null) { + out.write(sha256.digest(associatedData)); + } else { + out.write(sha256.digest()); + } + sha256.reset(); - byte[] calculateSignature(Map itemAttributes, - Map> attributeFlags, byte[] associatedData, - SecretKey key) throws GeneralSecurityException { - if (key instanceof DelegatedKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey)key); + for (String name : attrNames) { + Set set = attributeFlags.get(name); + if (set != null && set.contains(EncryptionFlags.SIGN)) { + AttributeValue tmp = itemAttributes.get(name); + out.write(sha256.digest(name.getBytes(UTF8))); + sha256.reset(); + if (set.contains(EncryptionFlags.ENCRYPT)) { + sha256.update("ENCRYPTED".getBytes(UTF8)); + } else { + sha256.update("PLAINTEXT".getBytes(UTF8)); + } + out.write(sha256.digest()); + + sha256.reset(); + + sha256.update(AttributeValueMarshaller.marshall(tmp)); + out.write(sha256.digest()); + sha256.reset(); } - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - Mac hmac = Mac.getInstance(key.getAlgorithm()); - hmac.init(key); - hmac.update(stringToSign); - return hmac.doFinal(); + } + return out.toByteArray(); + } catch (IOException ex) { + // Due to the objects in use, an IOException is not possible. + throw new RuntimeException("Unexpected exception", ex); } + } - byte[] calculateSignature(Map itemAttributes, - Map> attributeFlags, byte[] associatedData, - PrivateKey key) throws GeneralSecurityException { - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - Signature sig = Signature.getInstance(signingAlgorithm); - sig.initSign(key, rnd); - sig.update(stringToSign); - return sig.sign(); + /** The itemAttributes have already been encrypted, if necessary, before the signing. */ + byte[] calculateSignature( + Map itemAttributes, + Map> attributeFlags, + byte[] associatedData, + Key key) + throws GeneralSecurityException { + if (key instanceof DelegatedKey) { + return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key); + } else if (key instanceof SecretKey) { + return calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey) key); + } else if (key instanceof PrivateKey) { + return calculateSignature(itemAttributes, attributeFlags, associatedData, (PrivateKey) key); + } else { + throw new IllegalArgumentException("No integrity key provided"); } + } + + byte[] calculateSignature( + Map itemAttributes, + Map> attributeFlags, + byte[] associatedData, + DelegatedKey key) + throws GeneralSecurityException { + byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); + return key.sign(stringToSign, key.getAlgorithm()); + } - String getSigningAlgorithm() { - return signingAlgorithm; + byte[] calculateSignature( + Map itemAttributes, + Map> attributeFlags, + byte[] associatedData, + SecretKey key) + throws GeneralSecurityException { + if (key instanceof DelegatedKey) { + return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key); } + byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); + Mac hmac = Mac.getInstance(key.getAlgorithm()); + hmac.init(key); + hmac.update(stringToSign); + return hmac.doFinal(); + } - /** - * Constant-time equality check. - */ - private boolean safeEquals(ByteBuffer signature, byte[] calculatedSig) { - try { - signature.rewind(); - Mac hmac = Mac.getInstance(hmacComparisonKey.getAlgorithm()); - hmac.init(hmacComparisonKey); - hmac.update(signature); - byte[] signatureHash = hmac.doFinal(); - - hmac.reset(); - hmac.update(calculatedSig); - byte[] calculatedHash = hmac.doFinal(); - - return MessageDigest.isEqual(signatureHash, calculatedHash); - } catch (GeneralSecurityException ex) { - // We've hardcoded these algorithms, so the error should not be possible. - throw new RuntimeException("Unexpected exception", ex); - } + byte[] calculateSignature( + Map itemAttributes, + Map> attributeFlags, + byte[] associatedData, + PrivateKey key) + throws GeneralSecurityException { + byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); + Signature sig = Signature.getInstance(signingAlgorithm); + sig.initSign(key, rnd); + sig.update(stringToSign); + return sig.sign(); + } + + String getSigningAlgorithm() { + return signingAlgorithm; + } + + /** Constant-time equality check. */ + private boolean safeEquals(ByteBuffer signature, byte[] calculatedSig) { + try { + signature.rewind(); + Mac hmac = Mac.getInstance(hmacComparisonKey.getAlgorithm()); + hmac.init(hmacComparisonKey); + hmac.update(signature); + byte[] signatureHash = hmac.doFinal(); + + hmac.reset(); + hmac.update(calculatedSig); + byte[] calculatedHash = hmac.doFinal(); + + return MessageDigest.isEqual(signatureHash, calculatedHash); + } catch (GeneralSecurityException ex) { + // We've hardcoded these algorithms, so the error should not be possible. + throw new RuntimeException("Unexpected exception", ex); } + } - private static byte[] toByteArray(ByteBuffer buffer) { - if (buffer.hasArray()) { - byte[] result = buffer.array(); - buffer.rewind(); - return result; - } else { - byte[] result = new byte[buffer.remaining()]; - buffer.get(result); - buffer.rewind(); - return result; - } + private static byte[] toByteArray(ByteBuffer buffer) { + if (buffer.hasArray()) { + byte[] result = buffer.array(); + buffer.rewind(); + return result; + } else { + byte[] result = new byte[buffer.remaining()]; + buffer.get(result); + buffer.rewind(); + return result; } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java index 4340598e..c36a7664 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java @@ -14,212 +14,207 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** - * This class serves to provide additional useful data to - * {@link EncryptionMaterialsProvider}s so they can more intelligently select - * the proper {@link EncryptionMaterials} or {@link DecryptionMaterials} for - * use. Any of the methods are permitted to return null. - *

- * For the simplest cases, all a developer needs to provide in the context are: + * This class serves to provide additional useful data to {@link EncryptionMaterialsProvider}s so + * they can more intelligently select the proper {@link EncryptionMaterials} or {@link + * DecryptionMaterials} for use. Any of the methods are permitted to return null. + * + *

For the simplest cases, all a developer needs to provide in the context are: + * *

    - *
  • TableName
  • - *
  • HashKeyName
  • - *
  • RangeKeyName (if present)
  • + *
  • TableName + *
  • HashKeyName + *
  • RangeKeyName (if present) *
- * + * * This class is immutable. - * - * @author Greg Rubin + * + * @author Greg Rubin */ public final class EncryptionContext { - private final String tableName; - private final Map attributeValues; - private final Class modeledClass; - private final Object developerContext; - private final String hashKeyName; - private final String rangeKeyName; - private final Map materialDescription; - - private EncryptionContext(Builder builder) { - tableName = builder.getTableName(); - attributeValues = builder.getAttributeValues(); - modeledClass = builder.getModeledClass(); - developerContext = builder.getDeveloperContext(); - hashKeyName = builder.getHashKeyName(); - rangeKeyName = builder.getRangeKeyName(); - materialDescription = builder.getMaterialDescription(); - } - - /** - * Returns the name of the DynamoDB Table this record is associated with. - */ + private final String tableName; + private final Map attributeValues; + private final Class modeledClass; + private final Object developerContext; + private final String hashKeyName; + private final String rangeKeyName; + private final Map materialDescription; + + private EncryptionContext(Builder builder) { + tableName = builder.getTableName(); + attributeValues = builder.getAttributeValues(); + modeledClass = builder.getModeledClass(); + developerContext = builder.getDeveloperContext(); + hashKeyName = builder.getHashKeyName(); + rangeKeyName = builder.getRangeKeyName(); + materialDescription = builder.getMaterialDescription(); + } + + /** Returns the name of the DynamoDB Table this record is associated with. */ + public String getTableName() { + return tableName; + } + + /** Returns the DynamoDB record about to be encrypted/decrypted. */ + public Map getAttributeValues() { + return attributeValues; + } + + /** + * When used for an object mapping layer (such as {@link DynamoDBMapper}) this represents the + * class being mapped to/from DynamoDB. + */ + public Class getModeledClass() { + return modeledClass; + } + + /** + * This object has no meaning (and will not be set or examined) by any core libraries. It exists + * to allow custom object mappers and data access layers to pass data to {@link + * EncryptionMaterialsProvider}s through the {@link DynamoDBEncryptor}. + */ + public Object getDeveloperContext() { + return developerContext; + } + + /** Returns the name of the HashKey attribute for the record to be encrypted/decrypted. */ + public String getHashKeyName() { + return hashKeyName; + } + + /** Returns the name of the RangeKey attribute for the record to be encrypted/decrypted. */ + public String getRangeKeyName() { + return rangeKeyName; + } + + public Map getMaterialDescription() { + return materialDescription; + } + + /** + * Builder class for {@link EncryptionContext}. Mutable objects (other than developerContext + * ) will undergo a defensive copy prior to being stored in the builder. + * + *

This class is not thread-safe. + */ + public static final class Builder { + private String tableName = null; + private Map attributeValues = null; + private Class modeledClass = null; + private Object developerContext = null; + private String hashKeyName = null; + private String rangeKeyName = null; + private Map materialDescription = null; + + /** Defaults all fields to null. */ + public Builder() {} + + /** Copy constructor. This will perform a shallow copy of the DeveloperContext. */ + public Builder(EncryptionContext context) { + tableName = context.getTableName(); + attributeValues = context.getAttributeValues(); + modeledClass = context.getModeledClass(); + developerContext = context.getDeveloperContext(); + hashKeyName = context.getHashKeyName(); + rangeKeyName = context.getRangeKeyName(); + materialDescription = context.getMaterialDescription(); + } + + public EncryptionContext build() { + return new EncryptionContext(this); + } + + public Builder withTableName(String tableName) { + this.tableName = tableName; + return this; + } + + public Builder withAttributeValues(Map attributeValues) { + this.attributeValues = + Collections.unmodifiableMap(new HashMap(attributeValues)); + return this; + } + + public Builder withModeledClass(Class modeledClass) { + this.modeledClass = modeledClass; + return this; + } + + public Builder withDeveloperContext(Object developerContext) { + this.developerContext = developerContext; + return this; + } + + public Builder withHashKeyName(String hashKeyName) { + this.hashKeyName = hashKeyName; + return this; + } + + public Builder withRangeKeyName(String rangeKeyName) { + this.rangeKeyName = rangeKeyName; + return this; + } + + public Builder withMaterialDescription(Map materialDescription) { + this.materialDescription = + Collections.unmodifiableMap(new HashMap(materialDescription)); + return this; + } + public String getTableName() { - return tableName; + return tableName; } - - /** - * Returns the DynamoDB record about to be encrypted/decrypted. - */ + public Map getAttributeValues() { - return attributeValues; + return attributeValues; } - - /** - * When used for an object mapping layer (such as {@link DynamoDBMapper}) - * this represents the class being mapped to/from DynamoDB. - */ + public Class getModeledClass() { - return modeledClass; - } - - /** - * This object has no meaning (and will not be set or examined) by any core libraries. - * It exists to allow custom object mappers and data access layers to pass - * data to {@link EncryptionMaterialsProvider}s through the {@link DynamoDBEncryptor}. - */ + return modeledClass; + } + public Object getDeveloperContext() { - return developerContext; + return developerContext; } - - /** - * Returns the name of the HashKey attribute for the record to be encrypted/decrypted. - */ + public String getHashKeyName() { - return hashKeyName; + return hashKeyName; } - /** - * Returns the name of the RangeKey attribute for the record to be encrypted/decrypted. - */ public String getRangeKeyName() { - return rangeKeyName; + return rangeKeyName; } public Map getMaterialDescription() { - return materialDescription; - } - - /** - * Builder class for {@link EncryptionContext}. - * Mutable objects (other than developerContext) will undergo - * a defensive copy prior to being stored in the builder. - * - * This class is not thread-safe. - */ - public static final class Builder { - private String tableName = null; - private Map attributeValues = null; - private Class modeledClass = null; - private Object developerContext = null; - private String hashKeyName = null; - private String rangeKeyName = null; - private Map materialDescription = null; - - /** - * Defaults all fields to null. - */ - public Builder() { - } - - /** - * Copy constructor. - * This will perform a shallow copy of the DeveloperContext. - */ - public Builder(EncryptionContext context) { - tableName = context.getTableName(); - attributeValues = context.getAttributeValues(); - modeledClass = context.getModeledClass(); - developerContext = context.getDeveloperContext(); - hashKeyName = context.getHashKeyName(); - rangeKeyName = context.getRangeKeyName(); - materialDescription = context.getMaterialDescription(); - } - - public EncryptionContext build() { - return new EncryptionContext(this); - } - - public Builder withTableName(String tableName) { - this.tableName = tableName; - return this; - } - - public Builder withAttributeValues(Map attributeValues) { - this.attributeValues = Collections.unmodifiableMap( - new HashMap(attributeValues)); - return this; - } - - public Builder withModeledClass(Class modeledClass) { - this.modeledClass = modeledClass; - return this; - } - - public Builder withDeveloperContext(Object developerContext) { - this.developerContext = developerContext; - return this; - } - - public Builder withHashKeyName(String hashKeyName) { - this.hashKeyName = hashKeyName; - return this; - } - - public Builder withRangeKeyName(String rangeKeyName) { - this.rangeKeyName = rangeKeyName; - return this; - } - - public Builder withMaterialDescription(Map materialDescription) { - this.materialDescription = Collections.unmodifiableMap( - new HashMap(materialDescription)); - return this; - } - - public String getTableName() { - return tableName; - } - - public Map getAttributeValues() { - return attributeValues; - } - - public Class getModeledClass() { - return modeledClass; - } - - public Object getDeveloperContext() { - return developerContext; - } - - public String getHashKeyName() { - return hashKeyName; - } - - public String getRangeKeyName() { - return rangeKeyName; - } - - public Map getMaterialDescription() { - return materialDescription; - } - } - - @Override - public String toString() { - return "EncryptionContext [tableName=" + tableName + ", attributeValues=" + attributeValues - + ", modeledClass=" + modeledClass + ", developerContext=" + developerContext - + ", hashKeyName=" + hashKeyName + ", rangeKeyName=" + rangeKeyName - + ", materialDescription=" + materialDescription + "]"; + return materialDescription; } + } + + @Override + public String toString() { + return "EncryptionContext [tableName=" + + tableName + + ", attributeValues=" + + attributeValues + + ", modeledClass=" + + modeledClass + + ", developerContext=" + + developerContext + + ", hashKeyName=" + + hashKeyName + + ", rangeKeyName=" + + rangeKeyName + + ", materialDescription=" + + materialDescription + + "]"; + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java index 4a946761..124365e9 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java @@ -14,10 +14,8 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public enum EncryptionFlags { - ENCRYPT, - SIGN + ENCRYPT, + SIGN } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java index fafa85b5..282faf10 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java @@ -20,21 +20,20 @@ import java.lang.annotation.Target; /** - * Marker annotation that indicates that attributes found during unmarshalling - * that are in the DynamoDB item but not modeled in the mapper model class - * should be included in for decryption/signature verification. The default - * behavior (without this annotation) is to ignore them, which can lead to - * signature verification failures when attributes are removed from model classes. - * - * If this annotation is added to a class with @DoNotEncrypt, then the unknown - * attributes will only be included in the signature calculation, and if it's - * added to a class with default encryption behavior, the unknown attributes - * will be signed and decrypted. + * Marker annotation that indicates that attributes found during unmarshalling that are in the + * DynamoDB item but not modeled in the mapper model class should be included in for + * decryption/signature verification. The default behavior (without this annotation) is to ignore + * them, which can lead to signature verification failures when attributes are removed from model + * classes. + * + *

If this annotation is added to a class with @DoNotEncrypt, then the unknown attributes will + * only be included in the signature calculation, and if it's added to a class with default + * encryption behavior, the unknown attributes will be signed and decrypted. + * + *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

- * * @author Dan Cavallaro */ @Target(value = {ElementType.TYPE}) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java index eb9c15db..ca5ad067 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java @@ -20,19 +20,18 @@ import java.lang.annotation.Target; /** - * Overrides the default tablename used as part of the data signature with - * {@code tableName} instead. This can be useful when multiple tables are - * used interchangably and data should be able to be copied or moved - * between them without needing to be reencrypted. + * Overrides the default tablename used as part of the data signature with {@code tableName} + * instead. This can be useful when multiple tables are used interchangably and data should be able + * to be copied or moved between them without needing to be reencrypted. * - *

For guidance on performing a safe data model change procedure, please see - * - * DynamoDB Encryption Client Developer Guide: Changing your data model

- * - * @author Greg Rubin + *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model + * + * @author Greg Rubin */ @Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface TableAadOverride { - String tableName(); + String tableName(); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java index b7fa559d..94637a32 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java @@ -19,55 +19,52 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; - import javax.crypto.SecretKey; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public abstract class AbstractRawMaterials implements DecryptionMaterials, EncryptionMaterials { - private Map description; - private final Key signingKey; - private final Key verificationKey; + private Map description; + private final Key signingKey; + private final Key verificationKey; - @SuppressWarnings("unchecked") - protected AbstractRawMaterials(KeyPair signingPair) { - this(signingPair, Collections.EMPTY_MAP); - } + @SuppressWarnings("unchecked") + protected AbstractRawMaterials(KeyPair signingPair) { + this(signingPair, Collections.EMPTY_MAP); + } - protected AbstractRawMaterials(KeyPair signingPair, Map description) { - this.signingKey = signingPair.getPrivate(); - this.verificationKey = signingPair.getPublic(); - setMaterialDescription(description); - } + protected AbstractRawMaterials(KeyPair signingPair, Map description) { + this.signingKey = signingPair.getPrivate(); + this.verificationKey = signingPair.getPublic(); + setMaterialDescription(description); + } - @SuppressWarnings("unchecked") - protected AbstractRawMaterials(SecretKey macKey) { - this(macKey, Collections.EMPTY_MAP); - } + @SuppressWarnings("unchecked") + protected AbstractRawMaterials(SecretKey macKey) { + this(macKey, Collections.EMPTY_MAP); + } - protected AbstractRawMaterials(SecretKey macKey, Map description) { - this.signingKey = macKey; - this.verificationKey = macKey; - this.description = Collections.unmodifiableMap(new HashMap(description)); - } + protected AbstractRawMaterials(SecretKey macKey, Map description) { + this.signingKey = macKey; + this.verificationKey = macKey; + this.description = Collections.unmodifiableMap(new HashMap(description)); + } - @Override - public Map getMaterialDescription() { - return new HashMap(description); - } + @Override + public Map getMaterialDescription() { + return new HashMap(description); + } - public void setMaterialDescription(Map description) { - this.description = Collections.unmodifiableMap(new HashMap(description)); - } + public void setMaterialDescription(Map description) { + this.description = Collections.unmodifiableMap(new HashMap(description)); + } - @Override - public Key getSigningKey() { - return signingKey; - } + @Override + public Key getSigningKey() { + return signingKey; + } - @Override - public Key getVerificationKey() { - return verificationKey; - } + @Override + public Key getVerificationKey() { + return verificationKey; + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java index 7e487913..cdd265d6 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java @@ -18,32 +18,31 @@ import java.security.KeyPair; import java.util.Collections; import java.util.Map; - import javax.crypto.SecretKey; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public class AsymmetricRawMaterials extends WrappedRawMaterials { - @SuppressWarnings("unchecked") - public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair) - throws GeneralSecurityException { - this(encryptionKey, signingPair, Collections.EMPTY_MAP); - } + @SuppressWarnings("unchecked") + public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair) + throws GeneralSecurityException { + this(encryptionKey, signingPair, Collections.EMPTY_MAP); + } - public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair, Map description) - throws GeneralSecurityException { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description); - } + public AsymmetricRawMaterials( + KeyPair encryptionKey, KeyPair signingPair, Map description) + throws GeneralSecurityException { + super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description); + } - @SuppressWarnings("unchecked") - public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey) - throws GeneralSecurityException { - this(encryptionKey, macKey, Collections.EMPTY_MAP); - } + @SuppressWarnings("unchecked") + public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey) + throws GeneralSecurityException { + this(encryptionKey, macKey, Collections.EMPTY_MAP); + } - public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey, Map description) - throws GeneralSecurityException { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description); - } + public AsymmetricRawMaterials( + KeyPair encryptionKey, SecretKey macKey, Map description) + throws GeneralSecurityException { + super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description); + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java index 58b6c089..8171415b 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java @@ -16,9 +16,7 @@ import java.util.Map; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public interface CryptographicMaterials { - public Map getMaterialDescription(); + public Map getMaterialDescription(); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java index 79d76c8d..6f94eb7f 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java @@ -15,13 +15,11 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials; import java.security.Key; - import javax.crypto.SecretKey; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public interface DecryptionMaterials extends CryptographicMaterials { - public SecretKey getDecryptionKey(); - public Key getVerificationKey(); + public SecretKey getDecryptionKey(); + + public Key getVerificationKey(); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java index 6486aae4..efd97480 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java @@ -15,13 +15,11 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials; import java.security.Key; - import javax.crypto.SecretKey; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public interface EncryptionMaterials extends CryptographicMaterials { - public SecretKey getEncryptionKey(); - public Key getSigningKey(); + public SecretKey getEncryptionKey(); + + public Key getSigningKey(); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java index 687299cb..a4da3c8b 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java @@ -17,42 +17,41 @@ import java.security.KeyPair; import java.util.Collections; import java.util.Map; - import javax.crypto.SecretKey; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public class SymmetricRawMaterials extends AbstractRawMaterials { - private final SecretKey cryptoKey; - - @SuppressWarnings("unchecked") - public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair) { - this(encryptionKey, signingPair, Collections.EMPTY_MAP); - } - - public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair, Map description) { - super(signingPair, description); - this.cryptoKey = encryptionKey; - } - - @SuppressWarnings("unchecked") - public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey) { - this(encryptionKey, macKey, Collections.EMPTY_MAP); - } - - public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey, Map description) { - super(macKey, description); - this.cryptoKey = encryptionKey; - } - - @Override - public SecretKey getEncryptionKey() { - return cryptoKey; - } - - @Override - public SecretKey getDecryptionKey() { - return cryptoKey; - } + private final SecretKey cryptoKey; + + @SuppressWarnings("unchecked") + public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair) { + this(encryptionKey, signingPair, Collections.EMPTY_MAP); + } + + public SymmetricRawMaterials( + SecretKey encryptionKey, KeyPair signingPair, Map description) { + super(signingPair, description); + this.cryptoKey = encryptionKey; + } + + @SuppressWarnings("unchecked") + public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey) { + this(encryptionKey, macKey, Collections.EMPTY_MAP); + } + + public SymmetricRawMaterials( + SecretKey encryptionKey, SecretKey macKey, Map description) { + super(macKey, description); + this.cryptoKey = encryptionKey; + } + + @Override + public SecretKey getEncryptionKey() { + return cryptoKey; + } + + @Override + public SecretKey getDecryptionKey() { + return cryptoKey; + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java index d70b5bd5..ba0c489a 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java @@ -17,12 +17,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DelegatedKey; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Base64; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; - -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.Key; @@ -30,177 +24,188 @@ import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.Map; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; /** - * Represents cryptographic materials used to manage unique record-level keys. - * This class specifically implements Envelope Encryption where a unique content - * key is randomly generated each time this class is constructed which is then - * encrypted with the Wrapping Key and then persisted in the Description. If a - * wrapped key is present in the Description, then that content key is unwrapped - * and used to decrypt the actual data in the record. - * - * Other possibly implementations might use a Key-Derivation Function to derive - * a unique key per record. + * Represents cryptographic materials used to manage unique record-level keys. This class + * specifically implements Envelope Encryption where a unique content key is randomly generated each + * time this class is constructed which is then encrypted with the Wrapping Key and then persisted + * in the Description. If a wrapped key is present in the Description, then that content key is + * unwrapped and used to decrypt the actual data in the record. + * + *

Other possibly implementations might use a Key-Derivation Function to derive a unique key per + * record. * - * @author Greg Rubin + * @author Greg Rubin */ public class WrappedRawMaterials extends AbstractRawMaterials { - /** - * The key-name in the Description which contains the algorithm use to wrap - * content key. Example values are "AESWrap", or - * "RSA/ECB/OAEPWithSHA-256AndMGF1Padding". - */ - public static final String KEY_WRAPPING_ALGORITHM = "amzn-ddb-wrap-alg"; - /** - * The key-name in the Description which contains the algorithm used by the - * content key. Example values are "AES", or "Blowfish". - */ - public static final String CONTENT_KEY_ALGORITHM = "amzn-ddb-env-alg"; - /** - * The key-name in the Description which which contains the wrapped content - * key. - */ - public static final String ENVELOPE_KEY = "amzn-ddb-env-key"; - private static final String DEFAULT_ALGORITHM = "AES/256"; - - protected final Key wrappingKey; - protected final Key unwrappingKey; - private final SecretKey envelopeKey; - - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair) - throws GeneralSecurityException { - this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap()); - } + /** + * The key-name in the Description which contains the algorithm use to wrap content key. Example + * values are "AESWrap", or "RSA/ECB/OAEPWithSHA-256AndMGF1Padding". + */ + public static final String KEY_WRAPPING_ALGORITHM = "amzn-ddb-wrap-alg"; + /** + * The key-name in the Description which contains the algorithm used by the content key. Example + * values are "AES", or "Blowfish". + */ + public static final String CONTENT_KEY_ALGORITHM = "amzn-ddb-env-alg"; + /** The key-name in the Description which which contains the wrapped content key. */ + public static final String ENVELOPE_KEY = "amzn-ddb-env-key"; - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair, - Map description) throws GeneralSecurityException { - super(signingPair, description); - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.envelopeKey = initEnvelopeKey(); - } + private static final String DEFAULT_ALGORITHM = "AES/256"; - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey) - throws GeneralSecurityException { - this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap()); - } + protected final Key wrappingKey; + protected final Key unwrappingKey; + private final SecretKey envelopeKey; - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey, - Map description) throws GeneralSecurityException { - super(macKey, description); - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.envelopeKey = initEnvelopeKey(); - } + public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair) + throws GeneralSecurityException { + this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap()); + } - @Override - public SecretKey getDecryptionKey() { - return envelopeKey; - } + public WrappedRawMaterials( + Key wrappingKey, Key unwrappingKey, KeyPair signingPair, Map description) + throws GeneralSecurityException { + super(signingPair, description); + this.wrappingKey = wrappingKey; + this.unwrappingKey = unwrappingKey; + this.envelopeKey = initEnvelopeKey(); + } + + public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey) + throws GeneralSecurityException { + this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap()); + } + + public WrappedRawMaterials( + Key wrappingKey, Key unwrappingKey, SecretKey macKey, Map description) + throws GeneralSecurityException { + super(macKey, description); + this.wrappingKey = wrappingKey; + this.unwrappingKey = unwrappingKey; + this.envelopeKey = initEnvelopeKey(); + } + + @Override + public SecretKey getDecryptionKey() { + return envelopeKey; + } - @Override - public SecretKey getEncryptionKey() { - return envelopeKey; + @Override + public SecretKey getEncryptionKey() { + return envelopeKey; + } + + /** + * Called by the constructors. If there is already a key associated with this record (usually + * signified by a value stored in the description in the key {@link #ENVELOPE_KEY}) it extracts it + * and returns it. Otherwise it generates a new key, stores a wrapped version in the Description, + * and returns the key to the caller. + * + * @return the content key (which is returned by both {@link #getDecryptionKey()} and {@link + * #getEncryptionKey()}. + * @throws GeneralSecurityException + */ + protected SecretKey initEnvelopeKey() throws GeneralSecurityException { + Map description = getMaterialDescription(); + if (description.containsKey(ENVELOPE_KEY)) { + if (unwrappingKey == null) { + throw new IllegalStateException("No private decryption key provided."); + } + byte[] encryptedKey = Base64.decode(description.get(ENVELOPE_KEY)); + String wrappingAlgorithm = unwrappingKey.getAlgorithm(); + if (description.containsKey(KEY_WRAPPING_ALGORITHM)) { + wrappingAlgorithm = description.get(KEY_WRAPPING_ALGORITHM); + } + return unwrapKey(description, encryptedKey, wrappingAlgorithm); + } else { + SecretKey key = + description.containsKey(CONTENT_KEY_ALGORITHM) + ? generateContentKey(description.get(CONTENT_KEY_ALGORITHM)) + : generateContentKey(DEFAULT_ALGORITHM); + + String wrappingAlg = + description.containsKey(KEY_WRAPPING_ALGORITHM) + ? description.get(KEY_WRAPPING_ALGORITHM) + : getTransformation(wrappingKey.getAlgorithm()); + byte[] encryptedKey = wrapKey(key, wrappingAlg); + description.put(ENVELOPE_KEY, Base64.encodeToString(encryptedKey)); + description.put(CONTENT_KEY_ALGORITHM, key.getAlgorithm()); + description.put(KEY_WRAPPING_ALGORITHM, wrappingAlg); + setMaterialDescription(description); + return key; } + } - /** - * Called by the constructors. If there is already a key associated with - * this record (usually signified by a value stored in the description in - * the key {@link #ENVELOPE_KEY}) it extracts it and returns it. Otherwise - * it generates a new key, stores a wrapped version in the Description, and - * returns the key to the caller. - * - * @return the content key (which is returned by both - * {@link #getDecryptionKey()} and {@link #getEncryptionKey()}. - * @throws GeneralSecurityException - */ - protected SecretKey initEnvelopeKey() throws GeneralSecurityException { - Map description = getMaterialDescription(); - if (description.containsKey(ENVELOPE_KEY)) { - if (unwrappingKey == null) { - throw new IllegalStateException("No private decryption key provided."); - } - byte[] encryptedKey = Base64.decode(description.get(ENVELOPE_KEY)); - String wrappingAlgorithm = unwrappingKey.getAlgorithm(); - if (description.containsKey(KEY_WRAPPING_ALGORITHM)) { - wrappingAlgorithm = description.get(KEY_WRAPPING_ALGORITHM); - } - return unwrapKey(description, encryptedKey, wrappingAlgorithm); - } else { - SecretKey key = description.containsKey(CONTENT_KEY_ALGORITHM) ? - generateContentKey(description.get(CONTENT_KEY_ALGORITHM)) : - generateContentKey(DEFAULT_ALGORITHM); - - String wrappingAlg = description.containsKey(KEY_WRAPPING_ALGORITHM) ? - description.get(KEY_WRAPPING_ALGORITHM) : - getTransformation(wrappingKey.getAlgorithm()); - byte[] encryptedKey = wrapKey(key, wrappingAlg); - description.put(ENVELOPE_KEY, Base64.encodeToString(encryptedKey)); - description.put(CONTENT_KEY_ALGORITHM, key.getAlgorithm()); - description.put(KEY_WRAPPING_ALGORITHM, wrappingAlg); - setMaterialDescription(description); - return key; - } + public byte[] wrapKey(SecretKey key, String wrappingAlg) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException { + if (wrappingKey instanceof DelegatedKey) { + return ((DelegatedKey) wrappingKey).wrap(key, null, wrappingAlg); + } else { + Cipher cipher = Cipher.getInstance(wrappingAlg); + cipher.init(Cipher.WRAP_MODE, wrappingKey, Utils.getRng()); + byte[] encryptedKey = cipher.wrap(key); + return encryptedKey; } + } + + protected SecretKey unwrapKey( + Map description, byte[] encryptedKey, String wrappingAlgorithm) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { + if (unwrappingKey instanceof DelegatedKey) { + return (SecretKey) + ((DelegatedKey) unwrappingKey) + .unwrap( + encryptedKey, + description.get(CONTENT_KEY_ALGORITHM), + Cipher.SECRET_KEY, + null, + wrappingAlgorithm); + } else { + Cipher cipher = Cipher.getInstance(wrappingAlgorithm); - public byte[] wrapKey(SecretKey key, String wrappingAlg) throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException, IllegalBlockSizeException { - if (wrappingKey instanceof DelegatedKey) { - return ((DelegatedKey)wrappingKey).wrap(key, null, wrappingAlg); - } else { - Cipher cipher = Cipher.getInstance(wrappingAlg); - cipher.init(Cipher.WRAP_MODE, wrappingKey, Utils.getRng()); - byte[] encryptedKey = cipher.wrap(key); - return encryptedKey; - } + // This can be of the form "AES/256" as well as "AES" e.g., + // but we want to set the SecretKey with just "AES" in either case + String[] algPieces = description.get(CONTENT_KEY_ALGORITHM).split("/", 2); + String contentKeyAlgorithm = algPieces[0]; + + cipher.init(Cipher.UNWRAP_MODE, unwrappingKey, Utils.getRng()); + return (SecretKey) cipher.unwrap(encryptedKey, contentKeyAlgorithm, Cipher.SECRET_KEY); } + } - protected SecretKey unwrapKey(Map description, byte[] encryptedKey, String wrappingAlgorithm) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { - if (unwrappingKey instanceof DelegatedKey) { - return (SecretKey)((DelegatedKey)unwrappingKey).unwrap(encryptedKey, - description.get(CONTENT_KEY_ALGORITHM), Cipher.SECRET_KEY, null, wrappingAlgorithm); - } else { - Cipher cipher = Cipher.getInstance(wrappingAlgorithm); - - // This can be of the form "AES/256" as well as "AES" e.g., - // but we want to set the SecretKey with just "AES" in either case - String[] algPieces = description.get(CONTENT_KEY_ALGORITHM).split("/", 2); - String contentKeyAlgorithm = algPieces[0]; - - cipher.init(Cipher.UNWRAP_MODE, unwrappingKey, Utils.getRng()); - return (SecretKey) cipher.unwrap(encryptedKey, - contentKeyAlgorithm, Cipher.SECRET_KEY); - } + protected SecretKey generateContentKey(final String algorithm) throws NoSuchAlgorithmException { + String[] pieces = algorithm.split("/", 2); + KeyGenerator kg = KeyGenerator.getInstance(pieces[0]); + int keyLen = 0; + if (pieces.length == 2) { + try { + keyLen = Integer.parseInt(pieces[1]); + } catch (NumberFormatException ex) { + keyLen = 0; + } } - - protected SecretKey generateContentKey(final String algorithm) throws NoSuchAlgorithmException { - String[] pieces = algorithm.split("/", 2); - KeyGenerator kg = KeyGenerator.getInstance(pieces[0]); - int keyLen = 0; - if (pieces.length == 2) { - try { - keyLen = Integer.parseInt(pieces[1]); - } catch (NumberFormatException ex) { - keyLen = 0; - } - } - - if (keyLen > 0) { - kg.init(keyLen, Utils.getRng()); - } else { - kg.init(Utils.getRng()); - } - return kg.generateKey(); + + if (keyLen > 0) { + kg.init(keyLen, Utils.getRng()); + } else { + kg.init(Utils.getRng()); } - - private static String getTransformation(final String algorithm) { - if (algorithm.equalsIgnoreCase("RSA")) { - return "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; - } else if (algorithm.equalsIgnoreCase("AES")) { - return "AESWrap"; - } else { - return algorithm; - } + return kg.generateKey(); + } + + private static String getTransformation(final String algorithm) { + if (algorithm.equalsIgnoreCase("RSA")) { + return "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; + } else if (algorithm.equalsIgnoreCase("AES")) { + return "AESWrap"; + } else { + return algorithm; } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProvider.java index 17d14fb7..490aab70 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProvider.java @@ -17,30 +17,31 @@ import java.security.KeyPair; import java.util.Collections; import java.util.Map; - import javax.crypto.SecretKey; /** - * This is a thin wrapper around the {@link WrappedMaterialsProvider}, using - * the provided encryptionKey for wrapping and unwrapping the - * record key. Please see that class for detailed documentation. - * - * @author Greg Rubin + * This is a thin wrapper around the {@link WrappedMaterialsProvider}, using the provided + * encryptionKey for wrapping and unwrapping the record key. Please see that class for + * detailed documentation. + * + * @author Greg Rubin */ public class AsymmetricStaticProvider extends WrappedMaterialsProvider { - public AsymmetricStaticProvider(KeyPair encryptionKey, KeyPair signingPair) { - this(encryptionKey, signingPair, Collections.emptyMap()); - } - - public AsymmetricStaticProvider(KeyPair encryptionKey, SecretKey macKey) { - this(encryptionKey, macKey, Collections.emptyMap()); - } - - public AsymmetricStaticProvider(KeyPair encryptionKey, KeyPair signingPair, Map description) { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description); - } + public AsymmetricStaticProvider(KeyPair encryptionKey, KeyPair signingPair) { + this(encryptionKey, signingPair, Collections.emptyMap()); + } + + public AsymmetricStaticProvider(KeyPair encryptionKey, SecretKey macKey) { + this(encryptionKey, macKey, Collections.emptyMap()); + } + + public AsymmetricStaticProvider( + KeyPair encryptionKey, KeyPair signingPair, Map description) { + super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description); + } - public AsymmetricStaticProvider(KeyPair encryptionKey, SecretKey macKey, Map description) { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description); - } + public AsymmetricStaticProvider( + KeyPair encryptionKey, SecretKey macKey, Map description) { + super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description); + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProvider.java index 1a575f8a..40a28327 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProvider.java @@ -2,182 +2,181 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.checkNotNull; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store.ProviderStore; import com.amazonaws.services.dynamodbv2.datamodeling.internal.TTLCache; import com.amazonaws.services.dynamodbv2.datamodeling.internal.TTLCache.EntryLoader; - import java.util.concurrent.TimeUnit; -import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.checkNotNull; - /** - * This meta-Provider encrypts data with the most recent version of keying materials from a - * {@link ProviderStore} and decrypts using whichever version is appropriate. It also caches the - * results from the {@link ProviderStore} to avoid excessive load on the backing systems. + * This meta-Provider encrypts data with the most recent version of keying materials from a {@link + * ProviderStore} and decrypts using whichever version is appropriate. It also caches the results + * from the {@link ProviderStore} to avoid excessive load on the backing systems. */ public class CachingMostRecentProvider implements EncryptionMaterialsProvider { - private static final long INITIAL_VERSION = 0; - private static final String PROVIDER_CACHE_KEY_DELIM = "#"; - private static final int DEFAULT_CACHE_MAX_SIZE = 1000; - - private final long ttlInNanos; - private final ProviderStore keystore; - protected final String defaultMaterialName; - private final TTLCache providerCache; - private final TTLCache versionCache; - - private final EntryLoader versionLoader = new EntryLoader() { + private static final long INITIAL_VERSION = 0; + private static final String PROVIDER_CACHE_KEY_DELIM = "#"; + private static final int DEFAULT_CACHE_MAX_SIZE = 1000; + + private final long ttlInNanos; + private final ProviderStore keystore; + protected final String defaultMaterialName; + private final TTLCache providerCache; + private final TTLCache versionCache; + + private final EntryLoader versionLoader = + new EntryLoader() { @Override public Long load(String entryKey) { - return keystore.getMaxVersion(entryKey); + return keystore.getMaxVersion(entryKey); } - }; + }; - private final EntryLoader providerLoader = new EntryLoader() { + private final EntryLoader providerLoader = + new EntryLoader() { @Override public EncryptionMaterialsProvider load(String entryKey) { - final String[] parts = entryKey.split(PROVIDER_CACHE_KEY_DELIM, 2); - if (parts.length != 2) { - throw new IllegalStateException("Invalid cache key for provider cache: " + entryKey); - } - return keystore.getProvider(parts[0], Long.parseLong(parts[1])); + final String[] parts = entryKey.split(PROVIDER_CACHE_KEY_DELIM, 2); + if (parts.length != 2) { + throw new IllegalStateException("Invalid cache key for provider cache: " + entryKey); + } + return keystore.getProvider(parts[0], Long.parseLong(parts[1])); } - }; - - - /** - * Creates a new {@link CachingMostRecentProvider}. - * - * @param keystore - * The key store that this provider will use to determine which material and which version of material to use - * @param materialName - * The name of the materials associated with this provider - * @param ttlInMillis - * The length of time in milliseconds to cache the most recent provider - */ - public CachingMostRecentProvider(final ProviderStore keystore, final String materialName, final long ttlInMillis) { - this(keystore, materialName, ttlInMillis, DEFAULT_CACHE_MAX_SIZE); - } - - /** - * Creates a new {@link CachingMostRecentProvider}. - * - * @param keystore - * The key store that this provider will use to determine which material and which version of material to use - * @param materialName - * The name of the materials associated with this provider - * @param ttlInMillis - * The length of time in milliseconds to cache the most recent provider - * @param maxCacheSize - * The maximum size of the underlying caches this provider uses. Entries will be evicted from the cache - * once this size is exceeded. - */ - public CachingMostRecentProvider(final ProviderStore keystore, final String materialName, final long ttlInMillis, final int maxCacheSize) { - this.keystore = checkNotNull(keystore, "keystore must not be null"); - this.defaultMaterialName = materialName; - this.ttlInNanos = TimeUnit.MILLISECONDS.toNanos(ttlInMillis); - - this.providerCache = new TTLCache<>(maxCacheSize, ttlInMillis, providerLoader); - this.versionCache = new TTLCache<>(maxCacheSize, ttlInMillis, versionLoader); - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - final String materialName = getMaterialName(context); - final long currentVersion = versionCache.load(materialName); - - if (currentVersion < 0) { - // The material hasn't been created yet, so specify a loading function - // to create the first version of materials and update both caches. - // We want this to be done as part of the cache load to ensure that this logic - // only happens once in a multithreaded environment, - // in order to limit calls to the keystore's dependencies. - final String cacheKey = buildCacheKey(materialName, INITIAL_VERSION); - EncryptionMaterialsProvider newProvider = providerCache.load( - cacheKey, - s -> { - // Create the new material in the keystore - final String[] parts = s.split(PROVIDER_CACHE_KEY_DELIM, 2); - if (parts.length != 2) { - throw new IllegalStateException("Invalid cache key for provider cache: " + s); - } - EncryptionMaterialsProvider provider = keystore.getOrCreate(parts[0], Long.parseLong(parts[1])); - - // We now should have version 0 in our keystore. - // Update the version cache for this material as a side effect - versionCache.put(materialName, INITIAL_VERSION); - - // Return the new materials to be put into the cache - return provider; - } - ); - - return newProvider.getEncryptionMaterials(context); - } else { - final String cacheKey = buildCacheKey(materialName, currentVersion); - return providerCache.load(cacheKey).getEncryptionMaterials(context); - } - } - - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - final long version = keystore.getVersionFromMaterialDescription( - context.getMaterialDescription()); - final String materialName = getMaterialName(context); - final String cacheKey = buildCacheKey(materialName, version); - - EncryptionMaterialsProvider provider = providerCache.load(cacheKey); - return provider.getDecryptionMaterials(context); + }; + + /** + * Creates a new {@link CachingMostRecentProvider}. + * + * @param keystore The key store that this provider will use to determine which material and which + * version of material to use + * @param materialName The name of the materials associated with this provider + * @param ttlInMillis The length of time in milliseconds to cache the most recent provider + */ + public CachingMostRecentProvider( + final ProviderStore keystore, final String materialName, final long ttlInMillis) { + this(keystore, materialName, ttlInMillis, DEFAULT_CACHE_MAX_SIZE); + } + + /** + * Creates a new {@link CachingMostRecentProvider}. + * + * @param keystore The key store that this provider will use to determine which material and which + * version of material to use + * @param materialName The name of the materials associated with this provider + * @param ttlInMillis The length of time in milliseconds to cache the most recent provider + * @param maxCacheSize The maximum size of the underlying caches this provider uses. Entries will + * be evicted from the cache once this size is exceeded. + */ + public CachingMostRecentProvider( + final ProviderStore keystore, + final String materialName, + final long ttlInMillis, + final int maxCacheSize) { + this.keystore = checkNotNull(keystore, "keystore must not be null"); + this.defaultMaterialName = materialName; + this.ttlInNanos = TimeUnit.MILLISECONDS.toNanos(ttlInMillis); + + this.providerCache = new TTLCache<>(maxCacheSize, ttlInMillis, providerLoader); + this.versionCache = new TTLCache<>(maxCacheSize, ttlInMillis, versionLoader); + } + + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + final String materialName = getMaterialName(context); + final long currentVersion = versionCache.load(materialName); + + if (currentVersion < 0) { + // The material hasn't been created yet, so specify a loading function + // to create the first version of materials and update both caches. + // We want this to be done as part of the cache load to ensure that this logic + // only happens once in a multithreaded environment, + // in order to limit calls to the keystore's dependencies. + final String cacheKey = buildCacheKey(materialName, INITIAL_VERSION); + EncryptionMaterialsProvider newProvider = + providerCache.load( + cacheKey, + s -> { + // Create the new material in the keystore + final String[] parts = s.split(PROVIDER_CACHE_KEY_DELIM, 2); + if (parts.length != 2) { + throw new IllegalStateException("Invalid cache key for provider cache: " + s); + } + EncryptionMaterialsProvider provider = + keystore.getOrCreate(parts[0], Long.parseLong(parts[1])); + + // We now should have version 0 in our keystore. + // Update the version cache for this material as a side effect + versionCache.put(materialName, INITIAL_VERSION); + + // Return the new materials to be put into the cache + return provider; + }); + + return newProvider.getEncryptionMaterials(context); + } else { + final String cacheKey = buildCacheKey(materialName, currentVersion); + return providerCache.load(cacheKey).getEncryptionMaterials(context); } - - /** - * Completely empties the cache of both the current and old versions. - */ - @Override - public void refresh() { - versionCache.clear(); - providerCache.clear(); - } - - public String getMaterialName() { - return defaultMaterialName; - } - - public long getTtlInMills() { - return TimeUnit.NANOSECONDS.toMillis(ttlInNanos); - } - - /** - * The current version of the materials being used for encryption. Returns -1 if we do not - * currently have a current version. - */ - public long getCurrentVersion() { - return versionCache.load(getMaterialName()); - } - - /** - * The last time the current version was updated. Returns 0 if we do not currently have a - * current version. - */ - public long getLastUpdated() { - // We cache a version of -1 to mean that there is not a current version - if (versionCache.load(getMaterialName()) < 0) { - return 0; - } - // Otherwise, return the last update time of that entry - return TimeUnit.NANOSECONDS.toMillis(versionCache.getLastUpdated(getMaterialName())); - } - - protected String getMaterialName(final EncryptionContext context) { - return defaultMaterialName; - } - - private static String buildCacheKey(final String materialName, final long version) { - StringBuilder result = new StringBuilder(materialName); - result.append(PROVIDER_CACHE_KEY_DELIM); - result.append(version); - return result.toString(); + } + + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + final long version = + keystore.getVersionFromMaterialDescription(context.getMaterialDescription()); + final String materialName = getMaterialName(context); + final String cacheKey = buildCacheKey(materialName, version); + + EncryptionMaterialsProvider provider = providerCache.load(cacheKey); + return provider.getDecryptionMaterials(context); + } + + /** Completely empties the cache of both the current and old versions. */ + @Override + public void refresh() { + versionCache.clear(); + providerCache.clear(); + } + + public String getMaterialName() { + return defaultMaterialName; + } + + public long getTtlInMills() { + return TimeUnit.NANOSECONDS.toMillis(ttlInNanos); + } + + /** + * The current version of the materials being used for encryption. Returns -1 if we do not + * currently have a current version. + */ + public long getCurrentVersion() { + return versionCache.load(getMaterialName()); + } + + /** + * The last time the current version was updated. Returns 0 if we do not currently have a current + * version. + */ + public long getLastUpdated() { + // We cache a version of -1 to mean that there is not a current version + if (versionCache.load(getMaterialName()) < 0) { + return 0; } + // Otherwise, return the last update time of that entry + return TimeUnit.NANOSECONDS.toMillis(versionCache.getLastUpdated(getMaterialName())); + } + + protected String getMaterialName(final EncryptionContext context) { + return defaultMaterialName; + } + + private static String buildCacheKey(final String materialName, final long version) { + StringBuilder result = new StringBuilder(materialName); + result.append(PROVIDER_CACHE_KEY_DELIM); + result.append(version); + return result.toString(); + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java index a655fa86..aaec9361 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java @@ -14,6 +14,11 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.CONTENT_KEY_ALGORITHM; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.ENVELOPE_KEY; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.KEY_WRAPPING_ALGORITHM; +import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.loadVersion; + import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -30,288 +35,298 @@ import com.amazonaws.services.kms.model.GenerateDataKeyRequest; import com.amazonaws.services.kms.model.GenerateDataKeyResult; import com.amazonaws.util.StringUtils; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.CONTENT_KEY_ALGORITHM; -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.ENVELOPE_KEY; -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.KEY_WRAPPING_ALGORITHM; -import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.loadVersion; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; /** - * Generates a unique data key for each record in DynamoDB and protects that key - * using {@link AWSKMS}. Currently, the HashKey, RangeKey, and TableName will be - * included in the KMS EncryptionContext for wrapping/unwrapping the key. This - * means that records cannot be copied/moved between tables without re-encryption. + * Generates a unique data key for each record in DynamoDB and protects that key using {@link + * AWSKMS}. Currently, the HashKey, RangeKey, and TableName will be included in the KMS + * EncryptionContext for wrapping/unwrapping the key. This means that records cannot be copied/moved + * between tables without re-encryption. * - * @see KMS Encryption Context + * @see KMS + * Encryption Context */ public class DirectKmsMaterialProvider implements EncryptionMaterialsProvider { - static final String USER_AGENT_PREFIX = "DynamodbEncryptionSdkJava/"; - private static final String USER_AGENT = USER_AGENT_PREFIX + loadVersion(); - private static final String COVERED_ATTR_CTX_KEY = "aws-kms-ec-attr"; - private static final String SIGNING_KEY_ALGORITHM = "amzn-ddb-sig-alg"; - private static final String TABLE_NAME_EC_KEY = "*aws-kms-table*"; - - private static final String DEFAULT_ENC_ALG = "AES/256"; - private static final String DEFAULT_SIG_ALG = "HmacSHA256/256"; - private static final String KEY_COVERAGE = "*keys*"; - private static final String KDF_ALG = "HmacSHA256"; - private static final String KDF_SIG_INFO = "Signing"; - private static final String KDF_ENC_INFO = "Encryption"; - - private final AWSKMS kms; - private final String encryptionKeyId; - private final Map description; - private final String dataKeyAlg; - private final int dataKeyLength; - private final String dataKeyDesc; - private final String sigKeyAlg; - private final int sigKeyLength; - private final String sigKeyDesc; - - public DirectKmsMaterialProvider(AWSKMS kms) { - this(kms, null); + static final String USER_AGENT_PREFIX = "DynamodbEncryptionSdkJava/"; + private static final String USER_AGENT = USER_AGENT_PREFIX + loadVersion(); + private static final String COVERED_ATTR_CTX_KEY = "aws-kms-ec-attr"; + private static final String SIGNING_KEY_ALGORITHM = "amzn-ddb-sig-alg"; + private static final String TABLE_NAME_EC_KEY = "*aws-kms-table*"; + + private static final String DEFAULT_ENC_ALG = "AES/256"; + private static final String DEFAULT_SIG_ALG = "HmacSHA256/256"; + private static final String KEY_COVERAGE = "*keys*"; + private static final String KDF_ALG = "HmacSHA256"; + private static final String KDF_SIG_INFO = "Signing"; + private static final String KDF_ENC_INFO = "Encryption"; + + private final AWSKMS kms; + private final String encryptionKeyId; + private final Map description; + private final String dataKeyAlg; + private final int dataKeyLength; + private final String dataKeyDesc; + private final String sigKeyAlg; + private final int sigKeyLength; + private final String sigKeyDesc; + + public DirectKmsMaterialProvider(AWSKMS kms) { + this(kms, null); + } + + public DirectKmsMaterialProvider( + AWSKMS kms, String encryptionKeyId, Map materialDescription) { + this.kms = kms; + this.encryptionKeyId = encryptionKeyId; + this.description = + materialDescription != null + ? Collections.unmodifiableMap(new HashMap<>(materialDescription)) + : Collections.emptyMap(); + + dataKeyDesc = + description.containsKey(WrappedRawMaterials.CONTENT_KEY_ALGORITHM) + ? description.get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM) + : DEFAULT_ENC_ALG; + + String[] parts = dataKeyDesc.split("/", 2); + this.dataKeyAlg = parts[0]; + this.dataKeyLength = parts.length == 2 ? Integer.parseInt(parts[1]) : 256; + + sigKeyDesc = + description.containsKey(SIGNING_KEY_ALGORITHM) + ? description.get(SIGNING_KEY_ALGORITHM) + : DEFAULT_SIG_ALG; + + parts = sigKeyDesc.split("/", 2); + this.sigKeyAlg = parts[0]; + this.sigKeyLength = parts.length == 2 ? Integer.parseInt(parts[1]) : 256; + } + + public DirectKmsMaterialProvider(AWSKMS kms, String encryptionKeyId) { + this(kms, encryptionKeyId, Collections.emptyMap()); + } + + @Override + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + final Map materialDescription = context.getMaterialDescription(); + + final Map ec = new HashMap<>(); + final String providedEncAlg = materialDescription.get(CONTENT_KEY_ALGORITHM); + final String providedSigAlg = materialDescription.get(SIGNING_KEY_ALGORITHM); + + ec.put("*" + CONTENT_KEY_ALGORITHM + "*", providedEncAlg); + ec.put("*" + SIGNING_KEY_ALGORITHM + "*", providedSigAlg); + + populateKmsEcFromEc(context, ec); + + DecryptRequest request = appendUserAgent(new DecryptRequest()); + request.setCiphertextBlob( + ByteBuffer.wrap(Base64.decode(materialDescription.get(ENVELOPE_KEY)))); + request.setEncryptionContext(ec); + final DecryptResult decryptResult = decrypt(request, context); + validateEncryptionKeyId(decryptResult.getKeyId(), context); + + final Hkdf kdf; + try { + kdf = Hkdf.getInstance(KDF_ALG); + } catch (NoSuchAlgorithmException e) { + throw new DynamoDBMappingException(e); } - - public DirectKmsMaterialProvider(AWSKMS kms, String encryptionKeyId, Map materialDescription) { - this.kms = kms; - this.encryptionKeyId = encryptionKeyId; - this.description = materialDescription != null ? - Collections.unmodifiableMap(new HashMap<>(materialDescription)) : - Collections. emptyMap(); - - dataKeyDesc = description - .containsKey(WrappedRawMaterials.CONTENT_KEY_ALGORITHM) ? description - .get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM) : DEFAULT_ENC_ALG; - - String[] parts = dataKeyDesc.split("/", 2); - this.dataKeyAlg = parts[0]; - this.dataKeyLength = parts.length == 2 ? Integer.parseInt(parts[1]) : 256; - - sigKeyDesc = description - .containsKey(SIGNING_KEY_ALGORITHM) ? description - .get(SIGNING_KEY_ALGORITHM) : DEFAULT_SIG_ALG; - - parts = sigKeyDesc.split("/", 2); - this.sigKeyAlg = parts[0]; - this.sigKeyLength = parts.length == 2 ? Integer.parseInt(parts[1]) : 256; + kdf.init(toArray(decryptResult.getPlaintext())); + + final String[] encAlgParts = providedEncAlg.split("/", 2); + int encLength = encAlgParts.length == 2 ? Integer.parseInt(encAlgParts[1]) : 256; + final String[] sigAlgParts = providedSigAlg.split("/", 2); + int sigLength = sigAlgParts.length == 2 ? Integer.parseInt(sigAlgParts[1]) : 256; + + final SecretKey encryptionKey = + new SecretKeySpec(kdf.deriveKey(KDF_ENC_INFO, encLength / 8), encAlgParts[0]); + final SecretKey macKey = + new SecretKeySpec(kdf.deriveKey(KDF_SIG_INFO, sigLength / 8), sigAlgParts[0]); + + return new SymmetricRawMaterials(encryptionKey, macKey, materialDescription); + } + + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + final Map ec = new HashMap<>(); + ec.put("*" + CONTENT_KEY_ALGORITHM + "*", dataKeyDesc); + ec.put("*" + SIGNING_KEY_ALGORITHM + "*", sigKeyDesc); + populateKmsEcFromEc(context, ec); + + final String keyId = selectEncryptionKeyId(context); + if (StringUtils.isNullOrEmpty(keyId)) { + throw new DynamoDBMappingException("Encryption key id is empty."); } - public DirectKmsMaterialProvider(AWSKMS kms, String encryptionKeyId) { - this(kms, encryptionKeyId, Collections. emptyMap()); + final GenerateDataKeyRequest req = appendUserAgent(new GenerateDataKeyRequest()); + req.setKeyId(keyId); + // NumberOfBytes parameter is used because we're not using this key as an AES-256 key, + // we're using it as an HKDF-SHA256 key. + req.setNumberOfBytes(256 / 8); + req.setEncryptionContext(ec); + + final GenerateDataKeyResult dataKeyResult = generateDataKey(req, context); + + final Map materialDescription = new HashMap<>(); + materialDescription.putAll(description); + materialDescription.put(COVERED_ATTR_CTX_KEY, KEY_COVERAGE); + materialDescription.put(KEY_WRAPPING_ALGORITHM, "kms"); + materialDescription.put(CONTENT_KEY_ALGORITHM, dataKeyDesc); + materialDescription.put(SIGNING_KEY_ALGORITHM, sigKeyDesc); + materialDescription.put( + ENVELOPE_KEY, Base64.encodeToString(toArray(dataKeyResult.getCiphertextBlob()))); + + final Hkdf kdf; + try { + kdf = Hkdf.getInstance(KDF_ALG); + } catch (NoSuchAlgorithmException e) { + throw new DynamoDBMappingException(e); } - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - final Map materialDescription = context.getMaterialDescription(); - - final Map ec = new HashMap<>(); - final String providedEncAlg = materialDescription.get(CONTENT_KEY_ALGORITHM); - final String providedSigAlg = materialDescription.get(SIGNING_KEY_ALGORITHM); - - ec.put("*" + CONTENT_KEY_ALGORITHM + "*", providedEncAlg); - ec.put("*" + SIGNING_KEY_ALGORITHM + "*", providedSigAlg); - - populateKmsEcFromEc(context, ec); - - DecryptRequest request = appendUserAgent(new DecryptRequest()); - request.setCiphertextBlob(ByteBuffer.wrap(Base64.decode(materialDescription.get(ENVELOPE_KEY)))); - request.setEncryptionContext(ec); - final DecryptResult decryptResult = decrypt(request, context); - validateEncryptionKeyId(decryptResult.getKeyId(), context); - - final Hkdf kdf; - try { - kdf = Hkdf.getInstance(KDF_ALG); - } catch (NoSuchAlgorithmException e) { - throw new DynamoDBMappingException(e); - } - kdf.init(toArray(decryptResult.getPlaintext())); - - final String[] encAlgParts = providedEncAlg.split("/", 2); - int encLength = encAlgParts.length == 2 ? Integer.parseInt(encAlgParts[1]) : 256; - final String[] sigAlgParts = providedSigAlg.split("/", 2); - int sigLength = sigAlgParts.length == 2 ? Integer.parseInt(sigAlgParts[1]) : 256; - - final SecretKey encryptionKey = new SecretKeySpec(kdf.deriveKey(KDF_ENC_INFO, encLength / 8), encAlgParts[0]); - final SecretKey macKey = new SecretKeySpec(kdf.deriveKey(KDF_SIG_INFO, sigLength / 8), sigAlgParts[0]); - - return new SymmetricRawMaterials(encryptionKey, macKey, materialDescription); - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - final Map ec = new HashMap<>(); - ec.put("*" + CONTENT_KEY_ALGORITHM + "*", dataKeyDesc); - ec.put("*" + SIGNING_KEY_ALGORITHM + "*", sigKeyDesc); - populateKmsEcFromEc(context, ec); - - final String keyId = selectEncryptionKeyId(context); - if (StringUtils.isNullOrEmpty(keyId)) { - throw new DynamoDBMappingException("Encryption key id is empty."); - } - - final GenerateDataKeyRequest req = appendUserAgent(new GenerateDataKeyRequest()); - req.setKeyId(keyId); - // NumberOfBytes parameter is used because we're not using this key as an AES-256 key, - // we're using it as an HKDF-SHA256 key. - req.setNumberOfBytes(256 / 8); - req.setEncryptionContext(ec); - - final GenerateDataKeyResult dataKeyResult = generateDataKey(req, context); - - final Map materialDescription = new HashMap<>(); - materialDescription.putAll(description); - materialDescription.put(COVERED_ATTR_CTX_KEY, KEY_COVERAGE); - materialDescription.put(KEY_WRAPPING_ALGORITHM, "kms"); - materialDescription.put(CONTENT_KEY_ALGORITHM, dataKeyDesc); - materialDescription.put(SIGNING_KEY_ALGORITHM, sigKeyDesc); - materialDescription.put(ENVELOPE_KEY, Base64.encodeToString(toArray(dataKeyResult.getCiphertextBlob()))); - - final Hkdf kdf; - try { - kdf = Hkdf.getInstance(KDF_ALG); - } catch (NoSuchAlgorithmException e) { - throw new DynamoDBMappingException(e); - } - - kdf.init(toArray(dataKeyResult.getPlaintext())); - - final SecretKey encryptionKey = new SecretKeySpec(kdf.deriveKey(KDF_ENC_INFO, dataKeyLength / 8), dataKeyAlg); - final SecretKey signatureKey = new SecretKeySpec(kdf.deriveKey(KDF_SIG_INFO, sigKeyLength / 8), sigKeyAlg); - return new SymmetricRawMaterials(encryptionKey, signatureKey, materialDescription); - } - - /** - * Get encryption key id that is used to create the {@link EncryptionMaterials}. - * - * @return encryption key id. - */ - protected String getEncryptionKeyId() { - return this.encryptionKeyId; - } - - /** - * Select encryption key id to be used to generate data key. The default implementation of this method returns - * {@link DirectKmsMaterialProvider#encryptionKeyId}. - * - * @param context encryption context. - * @return the encryptionKeyId. - * @throws DynamoDBMappingException when we fails to select a valid encryption key id. - */ - protected String selectEncryptionKeyId(EncryptionContext context) throws DynamoDBMappingException { - return getEncryptionKeyId(); + kdf.init(toArray(dataKeyResult.getPlaintext())); + + final SecretKey encryptionKey = + new SecretKeySpec(kdf.deriveKey(KDF_ENC_INFO, dataKeyLength / 8), dataKeyAlg); + final SecretKey signatureKey = + new SecretKeySpec(kdf.deriveKey(KDF_SIG_INFO, sigKeyLength / 8), sigKeyAlg); + return new SymmetricRawMaterials(encryptionKey, signatureKey, materialDescription); + } + + /** + * Get encryption key id that is used to create the {@link EncryptionMaterials}. + * + * @return encryption key id. + */ + protected String getEncryptionKeyId() { + return this.encryptionKeyId; + } + + /** + * Select encryption key id to be used to generate data key. The default implementation of this + * method returns {@link DirectKmsMaterialProvider#encryptionKeyId}. + * + * @param context encryption context. + * @return the encryptionKeyId. + * @throws DynamoDBMappingException when we fails to select a valid encryption key id. + */ + protected String selectEncryptionKeyId(EncryptionContext context) + throws DynamoDBMappingException { + return getEncryptionKeyId(); + } + + /** + * Validate the encryption key id. The default implementation of this method does not validate + * encryption key id. + * + * @param encryptionKeyId encryption key id from {@link DecryptResult}. + * @param context encryption context. + * @throws DynamoDBMappingException when encryptionKeyId is invalid. + */ + protected void validateEncryptionKeyId(String encryptionKeyId, EncryptionContext context) + throws DynamoDBMappingException { + // No action taken. + } + + /** + * Decrypts ciphertext. The default implementation calls KMS to decrypt the ciphertext using the + * parameters provided in the {@link DecryptRequest}. Subclass can override the default + * implementation to provide additional request parameters using attributes within the {@link + * EncryptionContext}. + * + * @param request request parameters to decrypt the given ciphertext. + * @param context additional useful data to decrypt the ciphertext. + * @return the decrypted plaintext for the given ciphertext. + */ + protected DecryptResult decrypt(final DecryptRequest request, final EncryptionContext context) { + return kms.decrypt(request); + } + + /** + * Returns a data encryption key that you can use in your application to encrypt data locally. The + * default implementation calls KMS to generate the data key using the parameters provided in the + * {@link GenerateDataKeyRequest}. Subclass can override the default implementation to provide + * additional request parameters using attributes within the {@link EncryptionContext}. + * + * @param request request parameters to generate the data key. + * @param context additional useful data to generate the data key. + * @return the newly generated data key which includes both the plaintext and ciphertext. + */ + protected GenerateDataKeyResult generateDataKey( + final GenerateDataKeyRequest request, final EncryptionContext context) { + return kms.generateDataKey(request); + } + + /** + * Extracts relevant information from {@code context} and uses it to populate fields in {@code + * kmsEc}. Subclass can override the default implementation to provide an alternative encryption + * context in calls to KMS. Currently, the default implementation includes these fields: + * + *

+ *
{@code HashKeyName} + *
{@code HashKeyValue} + *
{@code RangeKeyName} + *
{@code RangeKeyValue} + *
{@link #TABLE_NAME_EC_KEY} + *
{@code TableName} + *
+ */ + protected void populateKmsEcFromEc(EncryptionContext context, Map kmsEc) { + final String hashKeyName = context.getHashKeyName(); + if (hashKeyName != null) { + final AttributeValue hashKey = context.getAttributeValues().get(hashKeyName); + if (hashKey.getN() != null) { + kmsEc.put(hashKeyName, hashKey.getN()); + } else if (hashKey.getS() != null) { + kmsEc.put(hashKeyName, hashKey.getS()); + } else if (hashKey.getB() != null) { + kmsEc.put(hashKeyName, Base64.encodeToString(toArray(hashKey.getB()))); + } else { + throw new UnsupportedOperationException( + "DirectKmsMaterialProvider only supports String, Number, and Binary HashKeys"); + } } - - /** - * Validate the encryption key id. The default implementation of this method does not validate - * encryption key id. - * - * @param encryptionKeyId encryption key id from {@link DecryptResult}. - * @param context encryption context. - * @throws DynamoDBMappingException when encryptionKeyId is invalid. - */ - protected void validateEncryptionKeyId(String encryptionKeyId, EncryptionContext context) - throws DynamoDBMappingException { - // No action taken. - } - - /** - * Decrypts ciphertext. The default implementation calls KMS to decrypt the ciphertext using the parameters - * provided in the {@link DecryptRequest}. Subclass can override the default implementation to provide - * additional request parameters using attributes within the {@link EncryptionContext}. - * - * @param request request parameters to decrypt the given ciphertext. - * @param context additional useful data to decrypt the ciphertext. - * @return the decrypted plaintext for the given ciphertext. - */ - protected DecryptResult decrypt(final DecryptRequest request, final EncryptionContext context) { - return kms.decrypt(request); - } - - /** - * Returns a data encryption key that you can use in your application to encrypt data locally. The default - * implementation calls KMS to generate the data key using the parameters provided in the - * {@link GenerateDataKeyRequest}. Subclass can override the default implementation to provide additional - * request parameters using attributes within the {@link EncryptionContext}. - * - * @param request request parameters to generate the data key. - * @param context additional useful data to generate the data key. - * @return the newly generated data key which includes both the plaintext and ciphertext. - */ - protected GenerateDataKeyResult generateDataKey(final GenerateDataKeyRequest request, - final EncryptionContext context) { - return kms.generateDataKey(request); - } - - /** - * Extracts relevant information from {@code context} and uses it to populate fields in - * {@code kmsEc}. Subclass can override the default implementation to provide an alternative - * encryption context in calls to KMS. Currently, the default implementation includes these fields: - *
- *
{@code HashKeyName}
- *
{@code HashKeyValue}
- *
{@code RangeKeyName}
- *
{@code RangeKeyValue}
- *
{@link #TABLE_NAME_EC_KEY}
- *
{@code TableName}
- *
- */ - protected void populateKmsEcFromEc(EncryptionContext context, Map kmsEc) { - final String hashKeyName = context.getHashKeyName(); - if (hashKeyName != null) { - final AttributeValue hashKey = context.getAttributeValues().get(hashKeyName); - if (hashKey.getN() != null) { - kmsEc.put(hashKeyName, hashKey.getN()); - } else if (hashKey.getS() != null) { - kmsEc.put(hashKeyName, hashKey.getS()); - } else if (hashKey.getB() != null) { - kmsEc.put(hashKeyName, Base64.encodeToString(toArray(hashKey.getB()))); - } else { - throw new UnsupportedOperationException("DirectKmsMaterialProvider only supports String, Number, and Binary HashKeys"); - } - } - final String rangeKeyName = context.getRangeKeyName(); - if (rangeKeyName != null) { - final AttributeValue rangeKey = context.getAttributeValues().get(rangeKeyName); - if (rangeKey.getN() != null) { - kmsEc.put(rangeKeyName, rangeKey.getN()); - } else if (rangeKey.getS() != null) { - kmsEc.put(rangeKeyName, rangeKey.getS()); - } else if (rangeKey.getB() != null) { - kmsEc.put(rangeKeyName, Base64.encodeToString(toArray(rangeKey.getB()))); - } else { - throw new UnsupportedOperationException("DirectKmsMaterialProvider only supports String, Number, and Binary RangeKeys"); - } - } - - final String tableName = context.getTableName(); - if (tableName != null) { - kmsEc.put(TABLE_NAME_EC_KEY, tableName); - } - } - - private static byte[] toArray(final ByteBuffer buff) { - final ByteBuffer dup = buff.asReadOnlyBuffer(); - byte[] result = new byte[dup.remaining()]; - dup.get(result); - return result; - } - - private static X appendUserAgent(final X request) { - request.getRequestClientOptions().appendUserAgent(USER_AGENT); - return request; + final String rangeKeyName = context.getRangeKeyName(); + if (rangeKeyName != null) { + final AttributeValue rangeKey = context.getAttributeValues().get(rangeKeyName); + if (rangeKey.getN() != null) { + kmsEc.put(rangeKeyName, rangeKey.getN()); + } else if (rangeKey.getS() != null) { + kmsEc.put(rangeKeyName, rangeKey.getS()); + } else if (rangeKey.getB() != null) { + kmsEc.put(rangeKeyName, Base64.encodeToString(toArray(rangeKey.getB()))); + } else { + throw new UnsupportedOperationException( + "DirectKmsMaterialProvider only supports String, Number, and Binary RangeKeys"); + } } - @Override - public void refresh() { - // No action needed + final String tableName = context.getTableName(); + if (tableName != null) { + kmsEc.put(TABLE_NAME_EC_KEY, tableName); } + } + + private static byte[] toArray(final ByteBuffer buff) { + final ByteBuffer dup = buff.asReadOnlyBuffer(); + byte[] result = new byte[dup.remaining()]; + dup.get(result); + return result; + } + + private static X appendUserAgent(final X request) { + request.getRequestClientOptions().appendUserAgent(USER_AGENT); + return request; + } + + @Override + public void refresh() { + // No action needed + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/EncryptionMaterialsProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/EncryptionMaterialsProvider.java index ed9efe19..3f7fc39f 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/EncryptionMaterialsProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/EncryptionMaterialsProvider.java @@ -19,53 +19,43 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; /** - * Interface for providing encryption materials. - * Implementations are free to use any strategy for providing encryption - * materials, such as simply providing static material that doesn't change, - * or more complicated implementations, such as integrating with existing - * key management systems. - * - * @author Greg Rubin + * Interface for providing encryption materials. Implementations are free to use any strategy for + * providing encryption materials, such as simply providing static material that doesn't change, or + * more complicated implementations, such as integrating with existing key management systems. + * + * @author Greg Rubin */ public interface EncryptionMaterialsProvider { - /** - * Retrieves encryption materials matching the specified description from some source. - * - * @param context - * Information to assist in selecting a the proper return value. The implementation - * is free to determine the minimum necessary for successful processing. - * - * @return - * The encryption materials that match the description, or null if no matching encryption materials found. - */ - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context); + /** + * Retrieves encryption materials matching the specified description from some source. + * + * @param context Information to assist in selecting a the proper return value. The implementation + * is free to determine the minimum necessary for successful processing. + * @return The encryption materials that match the description, or null if no matching encryption + * materials found. + */ + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context); - /** - * Returns EncryptionMaterials which the caller can use for encryption. - * Each implementation of EncryptionMaterialsProvider can choose its own - * strategy for loading encryption material. For example, an - * implementation might load encryption material from an existing key - * management system, or load new encryption material when keys are - * rotated. - * - * @param context - * Information to assist in selecting a the proper return value. The implementation - * is free to determine the minimum necessary for successful processing. - * - * @return EncryptionMaterials which the caller can use to encrypt or - * decrypt data. - */ - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context); + /** + * Returns EncryptionMaterials which the caller can use for encryption. Each implementation of + * EncryptionMaterialsProvider can choose its own strategy for loading encryption material. For + * example, an implementation might load encryption material from an existing key management + * system, or load new encryption material when keys are rotated. + * + * @param context Information to assist in selecting a the proper return value. The implementation + * is free to determine the minimum necessary for successful processing. + * @return EncryptionMaterials which the caller can use to encrypt or decrypt data. + */ + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context); - /** - * Forces this encryption materials provider to refresh its encryption - * material. For many implementations of encryption materials provider, - * this may simply be a no-op, such as any encryption materials provider - * implementation that vends static/non-changing encryption material. - * For other implementations that vend different encryption material - * throughout their lifetime, this method should force the encryption - * materials provider to refresh its encryption material. - */ - public void refresh(); + /** + * Forces this encryption materials provider to refresh its encryption material. For many + * implementations of encryption materials provider, this may simply be a no-op, such as any + * encryption materials provider implementation that vends static/non-changing encryption + * material. For other implementations that vend different encryption material throughout their + * lifetime, this method should force the encryption materials provider to refresh its encryption + * material. + */ + public void refresh(); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProvider.java index f16eb73b..389da763 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProvider.java @@ -14,6 +14,12 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.AsymmetricRawMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.SymmetricRawMaterials; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyStore; @@ -32,168 +38,167 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; -import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.AsymmetricRawMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.SymmetricRawMaterials; - -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public class KeyStoreMaterialsProvider implements EncryptionMaterialsProvider { - private final Map description; - private final String encryptionAlias; - private final String signingAlias; - private final ProtectionParameter encryptionProtection; - private final ProtectionParameter signingProtection; - private final KeyStore keyStore; - private final AtomicReference currMaterials = - new AtomicReference(); - - public KeyStoreMaterialsProvider(KeyStore keyStore, String encryptionAlias, String signingAlias, Map description) - throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { - this(keyStore, encryptionAlias, signingAlias, null, null, description); - } - - public KeyStoreMaterialsProvider(KeyStore keyStore, String encryptionAlias, String signingAlias, - ProtectionParameter encryptionProtection, ProtectionParameter signingProtection, - Map description) - throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { - super(); - this.keyStore = keyStore; - this.encryptionAlias = encryptionAlias; - this.signingAlias = signingAlias; - this.encryptionProtection = encryptionProtection; - this.signingProtection = signingProtection; - this.description = Collections.unmodifiableMap(new HashMap(description)); - - validateKeys(); - loadKeys(); - } - - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - CurrentMaterials materials = currMaterials.get(); - if (context.getMaterialDescription().entrySet().containsAll(description.entrySet())) { - if (materials.encryptionEntry instanceof SecretKeyEntry) { - return materials.symRawMaterials; - } else { - try { - return makeAsymMaterials(materials, context.getMaterialDescription()); - } catch (GeneralSecurityException ex) { - throw new DynamoDBMappingException("Unable to decrypt envelope key", ex); - } - } - } else { - return null; + private final Map description; + private final String encryptionAlias; + private final String signingAlias; + private final ProtectionParameter encryptionProtection; + private final ProtectionParameter signingProtection; + private final KeyStore keyStore; + private final AtomicReference currMaterials = + new AtomicReference(); + + public KeyStoreMaterialsProvider( + KeyStore keyStore, + String encryptionAlias, + String signingAlias, + Map description) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + this(keyStore, encryptionAlias, signingAlias, null, null, description); + } + + public KeyStoreMaterialsProvider( + KeyStore keyStore, + String encryptionAlias, + String signingAlias, + ProtectionParameter encryptionProtection, + ProtectionParameter signingProtection, + Map description) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + super(); + this.keyStore = keyStore; + this.encryptionAlias = encryptionAlias; + this.signingAlias = signingAlias; + this.encryptionProtection = encryptionProtection; + this.signingProtection = signingProtection; + this.description = Collections.unmodifiableMap(new HashMap(description)); + + validateKeys(); + loadKeys(); + } + + @Override + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + CurrentMaterials materials = currMaterials.get(); + if (context.getMaterialDescription().entrySet().containsAll(description.entrySet())) { + if (materials.encryptionEntry instanceof SecretKeyEntry) { + return materials.symRawMaterials; + } else { + try { + return makeAsymMaterials(materials, context.getMaterialDescription()); + } catch (GeneralSecurityException ex) { + throw new DynamoDBMappingException("Unable to decrypt envelope key", ex); } + } + } else { + return null; } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - CurrentMaterials materials = currMaterials.get(); - if (materials.encryptionEntry instanceof SecretKeyEntry) { - return materials.symRawMaterials; - } else { - try { - return makeAsymMaterials(materials, description); - } catch (GeneralSecurityException ex) { - throw new DynamoDBMappingException("Unable to encrypt envelope key", ex); - } - } + } + + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + CurrentMaterials materials = currMaterials.get(); + if (materials.encryptionEntry instanceof SecretKeyEntry) { + return materials.symRawMaterials; + } else { + try { + return makeAsymMaterials(materials, description); + } catch (GeneralSecurityException ex) { + throw new DynamoDBMappingException("Unable to encrypt envelope key", ex); + } } - - private AsymmetricRawMaterials makeAsymMaterials(CurrentMaterials materials, - Map description) throws GeneralSecurityException { - KeyPair encryptionPair = entry2Pair(materials.encryptionEntry); - if (materials.signingEntry instanceof SecretKeyEntry) { - return new AsymmetricRawMaterials(encryptionPair, - ((SecretKeyEntry) materials.signingEntry).getSecretKey(), description); - } else { - return new AsymmetricRawMaterials(encryptionPair, entry2Pair(materials.signingEntry), - description); - } + } + + private AsymmetricRawMaterials makeAsymMaterials( + CurrentMaterials materials, Map description) throws GeneralSecurityException { + KeyPair encryptionPair = entry2Pair(materials.encryptionEntry); + if (materials.signingEntry instanceof SecretKeyEntry) { + return new AsymmetricRawMaterials( + encryptionPair, ((SecretKeyEntry) materials.signingEntry).getSecretKey(), description); + } else { + return new AsymmetricRawMaterials( + encryptionPair, entry2Pair(materials.signingEntry), description); } - - private static KeyPair entry2Pair(Entry entry) { - PublicKey pub = null; - PrivateKey priv = null; - - if (entry instanceof PrivateKeyEntry) { - PrivateKeyEntry pk = (PrivateKeyEntry) entry; - if (pk.getCertificate() != null) { - pub = pk.getCertificate().getPublicKey(); - } - priv = pk.getPrivateKey(); - } else if (entry instanceof TrustedCertificateEntry) { - TrustedCertificateEntry tc = (TrustedCertificateEntry) entry; - pub = tc.getTrustedCertificate().getPublicKey(); - } else { - throw new IllegalArgumentException( - "Only entry types PrivateKeyEntry and TrustedCertificateEntry are supported."); - } - return new KeyPair(pub, priv); + } + + private static KeyPair entry2Pair(Entry entry) { + PublicKey pub = null; + PrivateKey priv = null; + + if (entry instanceof PrivateKeyEntry) { + PrivateKeyEntry pk = (PrivateKeyEntry) entry; + if (pk.getCertificate() != null) { + pub = pk.getCertificate().getPublicKey(); + } + priv = pk.getPrivateKey(); + } else if (entry instanceof TrustedCertificateEntry) { + TrustedCertificateEntry tc = (TrustedCertificateEntry) entry; + pub = tc.getTrustedCertificate().getPublicKey(); + } else { + throw new IllegalArgumentException( + "Only entry types PrivateKeyEntry and TrustedCertificateEntry are supported."); } - - /** - * Reloads the keys from the underlying keystore by calling - * {@link KeyStore#getEntry(String, ProtectionParameter)} again for each of them. - */ - @Override - public void refresh() { - try { - loadKeys(); - } catch (GeneralSecurityException ex) { - throw new DynamoDBMappingException("Unable to load keys from keystore", ex); - } + return new KeyPair(pub, priv); + } + + /** + * Reloads the keys from the underlying keystore by calling {@link KeyStore#getEntry(String, + * ProtectionParameter)} again for each of them. + */ + @Override + public void refresh() { + try { + loadKeys(); + } catch (GeneralSecurityException ex) { + throw new DynamoDBMappingException("Unable to load keys from keystore", ex); } + } - private void validateKeys() throws KeyStoreException { - if (!keyStore.containsAlias(encryptionAlias)) { - throw new IllegalArgumentException("Keystore does not contain alias: " - + encryptionAlias); - } - if (!keyStore.containsAlias(signingAlias)) { - throw new IllegalArgumentException("Keystore does not contain alias: " - + signingAlias); - } + private void validateKeys() throws KeyStoreException { + if (!keyStore.containsAlias(encryptionAlias)) { + throw new IllegalArgumentException("Keystore does not contain alias: " + encryptionAlias); } - - private void loadKeys() throws NoSuchAlgorithmException, UnrecoverableEntryException, - KeyStoreException { - Entry encryptionEntry = keyStore.getEntry(encryptionAlias, encryptionProtection); - Entry signingEntry = keyStore.getEntry(signingAlias, signingProtection); - CurrentMaterials newMaterials = new CurrentMaterials(encryptionEntry, signingEntry); - currMaterials.set(newMaterials); + if (!keyStore.containsAlias(signingAlias)) { + throw new IllegalArgumentException("Keystore does not contain alias: " + signingAlias); } - - private class CurrentMaterials { - public final Entry encryptionEntry; - public final Entry signingEntry; - public final SymmetricRawMaterials symRawMaterials; - - public CurrentMaterials(Entry encryptionEntry, Entry signingEntry) { - super(); - this.encryptionEntry = encryptionEntry; - this.signingEntry = signingEntry; - - if (encryptionEntry instanceof SecretKeyEntry) { - if (signingEntry instanceof SecretKeyEntry) { - this.symRawMaterials = new SymmetricRawMaterials( - ((SecretKeyEntry) encryptionEntry).getSecretKey(), - ((SecretKeyEntry) signingEntry).getSecretKey(), - description); - } else { - this.symRawMaterials = new SymmetricRawMaterials( - ((SecretKeyEntry) encryptionEntry).getSecretKey(), - entry2Pair(signingEntry), - description); - } - } else { - this.symRawMaterials = null; - } + } + + private void loadKeys() + throws NoSuchAlgorithmException, UnrecoverableEntryException, KeyStoreException { + Entry encryptionEntry = keyStore.getEntry(encryptionAlias, encryptionProtection); + Entry signingEntry = keyStore.getEntry(signingAlias, signingProtection); + CurrentMaterials newMaterials = new CurrentMaterials(encryptionEntry, signingEntry); + currMaterials.set(newMaterials); + } + + private class CurrentMaterials { + public final Entry encryptionEntry; + public final Entry signingEntry; + public final SymmetricRawMaterials symRawMaterials; + + public CurrentMaterials(Entry encryptionEntry, Entry signingEntry) { + super(); + this.encryptionEntry = encryptionEntry; + this.signingEntry = signingEntry; + + if (encryptionEntry instanceof SecretKeyEntry) { + if (signingEntry instanceof SecretKeyEntry) { + this.symRawMaterials = + new SymmetricRawMaterials( + ((SecretKeyEntry) encryptionEntry).getSecretKey(), + ((SecretKeyEntry) signingEntry).getSecretKey(), + description); + } else { + this.symRawMaterials = + new SymmetricRawMaterials( + ((SecretKeyEntry) encryptionEntry).getSecretKey(), + entry2Pair(signingEntry), + description); } + } else { + this.symRawMaterials = null; + } } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProvider.java index 068a6741..729ab0fc 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProvider.java @@ -14,117 +14,101 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; -import java.security.KeyPair; -import java.util.Collections; -import java.util.Map; - -import javax.crypto.SecretKey; - import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.CryptographicMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.SymmetricRawMaterials; +import java.security.KeyPair; +import java.util.Collections; +import java.util.Map; +import javax.crypto.SecretKey; /** - * A provider which always returns the same provided symmetric - * encryption/decryption key and the same signing/verification key(s). - * - * @author Greg Rubin + * A provider which always returns the same provided symmetric encryption/decryption key and the + * same signing/verification key(s). + * + * @author Greg Rubin */ public class SymmetricStaticProvider implements EncryptionMaterialsProvider { - private final SymmetricRawMaterials materials; + private final SymmetricRawMaterials materials; - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If - * only the public key is provided, then this provider may be - * used for decryption, but not encryption. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, KeyPair signingPair) { - this(encryptionKey, signingPair, Collections.emptyMap()); - } - - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If - * only the public key is provided, then this provider may be - * used for decryption, but not encryption. - * @param description - * the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for - * any {@link CryptographicMaterials} returned by this object. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, - KeyPair signingPair, Map description) { - materials = new SymmetricRawMaterials(encryptionKey, signingPair, - description); - } + /** + * @param encryptionKey the value to be returned by {@link + * #getEncryptionMaterials(EncryptionContext)} and {@link + * #getDecryptionMaterials(EncryptionContext)} + * @param signingPair the keypair used to sign/verify the data stored in Dynamo. If only the + * public key is provided, then this provider may be used for decryption, but not encryption. + */ + public SymmetricStaticProvider(SecretKey encryptionKey, KeyPair signingPair) { + this(encryptionKey, signingPair, Collections.emptyMap()); + } - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, SecretKey macKey) { - this(encryptionKey, macKey, Collections.emptyMap()); - } - - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - * @param description - * the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for - * any {@link CryptographicMaterials} returned by this object. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, SecretKey macKey, Map description) { - materials = new SymmetricRawMaterials(encryptionKey, macKey, description); - } + /** + * @param encryptionKey the value to be returned by {@link + * #getEncryptionMaterials(EncryptionContext)} and {@link + * #getDecryptionMaterials(EncryptionContext)} + * @param signingPair the keypair used to sign/verify the data stored in Dynamo. If only the + * public key is provided, then this provider may be used for decryption, but not encryption. + * @param description the value to be returned by {@link + * CryptographicMaterials#getMaterialDescription()} for any {@link CryptographicMaterials} + * returned by this object. + */ + public SymmetricStaticProvider( + SecretKey encryptionKey, KeyPair signingPair, Map description) { + materials = new SymmetricRawMaterials(encryptionKey, signingPair, description); + } - /** - * Returns the encryptionKey provided to the constructor if and only if - * materialDescription is a super-set (may be equal) to the - * description provided to the constructor. - */ - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - if (context.getMaterialDescription().entrySet().containsAll(materials.getMaterialDescription().entrySet())) { - return materials; - } - else { - return null; - } - } + /** + * @param encryptionKey the value to be returned by {@link + * #getEncryptionMaterials(EncryptionContext)} and {@link + * #getDecryptionMaterials(EncryptionContext)} + * @param macKey the key used to sign/verify the data stored in Dynamo. + */ + public SymmetricStaticProvider(SecretKey encryptionKey, SecretKey macKey) { + this(encryptionKey, macKey, Collections.emptyMap()); + } - /** - * Returns the encryptionKey provided to the constructor. - */ - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - return materials; - } - - /** - * Does nothing. - */ - @Override - public void refresh() { - // Do Nothing + /** + * @param encryptionKey the value to be returned by {@link + * #getEncryptionMaterials(EncryptionContext)} and {@link + * #getDecryptionMaterials(EncryptionContext)} + * @param macKey the key used to sign/verify the data stored in Dynamo. + * @param description the value to be returned by {@link + * CryptographicMaterials#getMaterialDescription()} for any {@link CryptographicMaterials} + * returned by this object. + */ + public SymmetricStaticProvider( + SecretKey encryptionKey, SecretKey macKey, Map description) { + materials = new SymmetricRawMaterials(encryptionKey, macKey, description); + } + + /** + * Returns the encryptionKey provided to the constructor if and only if + * materialDescription is a super-set (may be equal) to the description + * provided to the constructor. + */ + @Override + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + if (context + .getMaterialDescription() + .entrySet() + .containsAll(materials.getMaterialDescription().entrySet())) { + return materials; + } else { + return null; } + } + + /** Returns the encryptionKey provided to the constructor. */ + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + return materials; + } + + /** Does nothing. */ + @Override + public void refresh() { + // Do Nothing + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProvider.java index 222a9a1c..3dab893e 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProvider.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProvider.java @@ -14,150 +14,138 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.CryptographicMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyPair; import java.util.Collections; import java.util.HashMap; import java.util.Map; - import javax.crypto.SecretKey; -import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.CryptographicMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials; - /** - * This provider will use create a unique (random) symmetric key upon each call to - * {@link #getEncryptionMaterials(EncryptionContext)}. Practically, this means each record in DynamoDB will be - * encrypted under a unique record key. A wrapped/encrypted copy of this record key is stored in the - * MaterialsDescription field of that record and is unwrapped/decrypted upon reading that record. - * - * This is generally a more secure way of encrypting data than with the - * {@link SymmetricStaticProvider}. - * + * This provider will use create a unique (random) symmetric key upon each call to {@link + * #getEncryptionMaterials(EncryptionContext)}. Practically, this means each record in DynamoDB will + * be encrypted under a unique record key. A wrapped/encrypted copy of this record key is stored in + * the MaterialsDescription field of that record and is unwrapped/decrypted upon reading that + * record. + * + *

This is generally a more secure way of encrypting data than with the {@link + * SymmetricStaticProvider}. + * * @see WrappedRawMaterials - * - * @author Greg Rubin + * @author Greg Rubin */ public class WrappedMaterialsProvider implements EncryptionMaterialsProvider { - private final Key wrappingKey; - private final Key unwrappingKey; - private final KeyPair sigPair; - private final SecretKey macKey; - private final Map description; + private final Key wrappingKey; + private final Key unwrappingKey; + private final KeyPair sigPair; + private final SecretKey macKey; + private final Map description; - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If only the public key - * is provided, then this provider may only be used for decryption, but not - * encryption. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, KeyPair signingPair) { - this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap()); - } - - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If only the public key - * is provided, then this provider may only be used for decryption, but not - * encryption. - * @param description - * description the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for any - * {@link CryptographicMaterials} returned by this object. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, KeyPair signingPair, Map description) { - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.sigPair = signingPair; - this.macKey = null; - this.description = Collections.unmodifiableMap(new HashMap(description)); - } + /** + * @param wrappingKey The key used to wrap/encrypt the symmetric record key. (May be the same as + * the unwrappingKey.) + * @param unwrappingKey The key used to unwrap/decrypt the symmetric record key. (May be the same + * as the wrappingKey.) If null, then this provider may only be used for + * decryption, but not encryption. + * @param signingPair the keypair used to sign/verify the data stored in Dynamo. If only the + * public key is provided, then this provider may only be used for decryption, but not + * encryption. + */ + public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, KeyPair signingPair) { + this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap()); + } - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, SecretKey macKey) { - this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap()); - } - - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - * @param description - * description the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for any - * {@link CryptographicMaterials} returned by this object. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, SecretKey macKey, Map description) { - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.sigPair = null; - this.macKey = macKey; - this.description = Collections.unmodifiableMap(new HashMap(description)); - } + /** + * @param wrappingKey The key used to wrap/encrypt the symmetric record key. (May be the same as + * the unwrappingKey.) + * @param unwrappingKey The key used to unwrap/decrypt the symmetric record key. (May be the same + * as the wrappingKey.) If null, then this provider may only be used for + * decryption, but not encryption. + * @param signingPair the keypair used to sign/verify the data stored in Dynamo. If only the + * public key is provided, then this provider may only be used for decryption, but not + * encryption. + * @param description description the value to be returned by {@link + * CryptographicMaterials#getMaterialDescription()} for any {@link CryptographicMaterials} + * returned by this object. + */ + public WrappedMaterialsProvider( + Key wrappingKey, Key unwrappingKey, KeyPair signingPair, Map description) { + this.wrappingKey = wrappingKey; + this.unwrappingKey = unwrappingKey; + this.sigPair = signingPair; + this.macKey = null; + this.description = Collections.unmodifiableMap(new HashMap(description)); + } - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - try { - if (macKey != null) { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, macKey, context.getMaterialDescription()); - } else { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, sigPair, context.getMaterialDescription()); - } - } catch (GeneralSecurityException ex) { - throw new DynamoDBMappingException("Unable to decrypt envelope key", ex); - } - } + /** + * @param wrappingKey The key used to wrap/encrypt the symmetric record key. (May be the same as + * the unwrappingKey.) + * @param unwrappingKey The key used to unwrap/decrypt the symmetric record key. (May be the same + * as the wrappingKey.) If null, then this provider may only be used for + * decryption, but not encryption. + * @param macKey the key used to sign/verify the data stored in Dynamo. + */ + public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, SecretKey macKey) { + this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap()); + } - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - try { - if (macKey != null) { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, macKey, description); - } else { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, sigPair, description); - } - } catch (GeneralSecurityException ex) { - throw new DynamoDBMappingException("Unable to encrypt envelope key", ex); - } + /** + * @param wrappingKey The key used to wrap/encrypt the symmetric record key. (May be the same as + * the unwrappingKey.) + * @param unwrappingKey The key used to unwrap/decrypt the symmetric record key. (May be the same + * as the wrappingKey.) If null, then this provider may only be used for + * decryption, but not encryption. + * @param macKey the key used to sign/verify the data stored in Dynamo. + * @param description description the value to be returned by {@link + * CryptographicMaterials#getMaterialDescription()} for any {@link CryptographicMaterials} + * returned by this object. + */ + public WrappedMaterialsProvider( + Key wrappingKey, Key unwrappingKey, SecretKey macKey, Map description) { + this.wrappingKey = wrappingKey; + this.unwrappingKey = unwrappingKey; + this.sigPair = null; + this.macKey = macKey; + this.description = Collections.unmodifiableMap(new HashMap(description)); + } + + @Override + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + try { + if (macKey != null) { + return new WrappedRawMaterials( + wrappingKey, unwrappingKey, macKey, context.getMaterialDescription()); + } else { + return new WrappedRawMaterials( + wrappingKey, unwrappingKey, sigPair, context.getMaterialDescription()); + } + } catch (GeneralSecurityException ex) { + throw new DynamoDBMappingException("Unable to decrypt envelope key", ex); } - - @Override - public void refresh() { - // Do nothing + } + + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + try { + if (macKey != null) { + return new WrappedRawMaterials(wrappingKey, unwrappingKey, macKey, description); + } else { + return new WrappedRawMaterials(wrappingKey, unwrappingKey, sigPair, description); + } + } catch (GeneralSecurityException ex) { + throw new DynamoDBMappingException("Unable to encrypt envelope key", ex); } + } + + @Override + public void refresh() { + // Do nothing + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStore.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStore.java index 9406c801..940d2850 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStore.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStore.java @@ -12,21 +12,6 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - import com.amazonaws.AmazonClientException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; @@ -48,351 +33,397 @@ import com.amazonaws.services.dynamodbv2.model.PutItemRequest; import com.amazonaws.services.dynamodbv2.model.QueryRequest; import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; /** * Provides a simple collection of EncryptionMaterialProviders backed by an encrypted DynamoDB * table. This can be used to build key hierarchies or meta providers. * - * Currently, this only supports AES-256 in AESWrap mode and HmacSHA256 for the providers persisted - * in the table. + *

Currently, this only supports AES-256 in AESWrap mode and HmacSHA256 for the providers + * persisted in the table. * * @author rubin */ public class MetaStore extends ProviderStore { - private static final String INTEGRITY_ALGORITHM_FIELD = "intAlg"; - private static final String INTEGRITY_KEY_FIELD = "int"; - private static final String ENCRYPTION_ALGORITHM_FIELD = "encAlg"; - private static final String ENCRYPTION_KEY_FIELD = "enc"; - private static final Pattern COMBINED_PATTERN = Pattern.compile("([^#]+)#(\\d*)"); - private static final String DEFAULT_INTEGRITY = "HmacSHA256"; - private static final String DEFAULT_ENCRYPTION = "AES"; - private static final String MATERIAL_TYPE_VERSION = "t"; - private static final String META_ID = "amzn-ddb-meta-id"; - - private static final String DEFAULT_HASH_KEY = "N"; - private static final String DEFAULT_RANGE_KEY = "V"; - - /** Default no-op implementation of {@link ExtraDataSupplier}. */ - private static final EmptyExtraDataSupplier EMPTY_EXTRA_DATA_SUPPLIER - = new EmptyExtraDataSupplier(); - - /** DDB fields that must be encrypted. */ - private static final Set ENCRYPTED_FIELDS; - static { - final Set tempEncryptedFields = new HashSet<>(); - tempEncryptedFields.add(MATERIAL_TYPE_VERSION); - tempEncryptedFields.add(ENCRYPTION_KEY_FIELD); - tempEncryptedFields.add(ENCRYPTION_ALGORITHM_FIELD); - tempEncryptedFields.add(INTEGRITY_KEY_FIELD); - tempEncryptedFields.add(INTEGRITY_ALGORITHM_FIELD); - ENCRYPTED_FIELDS = tempEncryptedFields; - } - - private final Map doesNotExist; - private final Set doNotEncrypt; - private final String tableName; - private final AmazonDynamoDB ddb; - private final DynamoDBEncryptor encryptor; - private final EncryptionContext ddbCtx; - private final ExtraDataSupplier extraDataSupplier; + private static final String INTEGRITY_ALGORITHM_FIELD = "intAlg"; + private static final String INTEGRITY_KEY_FIELD = "int"; + private static final String ENCRYPTION_ALGORITHM_FIELD = "encAlg"; + private static final String ENCRYPTION_KEY_FIELD = "enc"; + private static final Pattern COMBINED_PATTERN = Pattern.compile("([^#]+)#(\\d*)"); + private static final String DEFAULT_INTEGRITY = "HmacSHA256"; + private static final String DEFAULT_ENCRYPTION = "AES"; + private static final String MATERIAL_TYPE_VERSION = "t"; + private static final String META_ID = "amzn-ddb-meta-id"; + + private static final String DEFAULT_HASH_KEY = "N"; + private static final String DEFAULT_RANGE_KEY = "V"; + + /** Default no-op implementation of {@link ExtraDataSupplier}. */ + private static final EmptyExtraDataSupplier EMPTY_EXTRA_DATA_SUPPLIER = + new EmptyExtraDataSupplier(); + + /** DDB fields that must be encrypted. */ + private static final Set ENCRYPTED_FIELDS; + + static { + final Set tempEncryptedFields = new HashSet<>(); + tempEncryptedFields.add(MATERIAL_TYPE_VERSION); + tempEncryptedFields.add(ENCRYPTION_KEY_FIELD); + tempEncryptedFields.add(ENCRYPTION_ALGORITHM_FIELD); + tempEncryptedFields.add(INTEGRITY_KEY_FIELD); + tempEncryptedFields.add(INTEGRITY_ALGORITHM_FIELD); + ENCRYPTED_FIELDS = tempEncryptedFields; + } + + private final Map doesNotExist; + private final Set doNotEncrypt; + private final String tableName; + private final AmazonDynamoDB ddb; + private final DynamoDBEncryptor encryptor; + private final EncryptionContext ddbCtx; + private final ExtraDataSupplier extraDataSupplier; + + /** Provides extra data that should be persisted along with the standard material data. */ + public interface ExtraDataSupplier { /** - * Provides extra data that should be persisted along with the standard material data. - */ - public interface ExtraDataSupplier { - - /** - * Gets the extra data attributes for the specified material name. - * - * @param materialName material name. - * @param version version number. - * @return plain text of the extra data. - */ - Map getAttributes(final String materialName, final long version); - - /** - * Gets the extra data field names that should be signed only but not encrypted. - * - * @return signed only fields. - */ - Set getSignedOnlyFieldNames(); - } - - /** - * Create a new MetaStore with specified table name. + * Gets the extra data attributes for the specified material name. * - * @param ddb Interface for accessing DynamoDB. - * @param tableName DynamoDB table name for this {@link MetaStore}. - * @param encryptor used to perform crypto operations on the record attributes. + * @param materialName material name. + * @param version version number. + * @return plain text of the extra data. */ - public MetaStore(final AmazonDynamoDB ddb, final String tableName, - final DynamoDBEncryptor encryptor) { - this(ddb, tableName, encryptor, EMPTY_EXTRA_DATA_SUPPLIER); - } + Map getAttributes(final String materialName, final long version); /** - * Create a new MetaStore with specified table name and extra data supplier. + * Gets the extra data field names that should be signed only but not encrypted. * - * @param ddb Interface for accessing DynamoDB. - * @param tableName DynamoDB table name for this {@link MetaStore}. - * @param encryptor used to perform crypto operations on the record attributes - * @param extraDataSupplier provides extra data that should be stored along with the material. + * @return signed only fields. */ - public MetaStore(final AmazonDynamoDB ddb, final String tableName, - final DynamoDBEncryptor encryptor, final ExtraDataSupplier extraDataSupplier) { - this.ddb = checkNotNull(ddb, "ddb must not be null"); - this.tableName = checkNotNull(tableName, "tableName must not be null"); - this.encryptor = checkNotNull(encryptor, "encryptor must not be null"); - this.extraDataSupplier = checkNotNull(extraDataSupplier, "extraDataSupplier must not be null"); - - this.ddbCtx = new EncryptionContext.Builder().withTableName(this.tableName) - .withHashKeyName(DEFAULT_HASH_KEY).withRangeKeyName(DEFAULT_RANGE_KEY).build(); - - final Map tmpExpected = new HashMap<>(); - tmpExpected.put(DEFAULT_HASH_KEY, new ExpectedAttributeValue().withExists(false)); - tmpExpected.put(DEFAULT_RANGE_KEY, new ExpectedAttributeValue().withExists(false)); - doesNotExist = Collections.unmodifiableMap(tmpExpected); - - this.doNotEncrypt = getSignedOnlyFields(extraDataSupplier); - } - - @Override - public EncryptionMaterialsProvider getProvider(final String materialName, final long version) { - Map item = getMaterialItem(materialName, version); - return decryptProvider(item); - } - - @Override - public EncryptionMaterialsProvider getOrCreate(final String materialName, final long nextId) { - final Map plaintext = createMaterialItem(materialName, nextId); - final Map ciphertext = conditionalPut(getEncryptedText(plaintext)); - return decryptProvider(ciphertext); - } - - @Override - public long getMaxVersion(final String materialName) { - final List> items = ddb.query( + Set getSignedOnlyFieldNames(); + } + + /** + * Create a new MetaStore with specified table name. + * + * @param ddb Interface for accessing DynamoDB. + * @param tableName DynamoDB table name for this {@link MetaStore}. + * @param encryptor used to perform crypto operations on the record attributes. + */ + public MetaStore( + final AmazonDynamoDB ddb, final String tableName, final DynamoDBEncryptor encryptor) { + this(ddb, tableName, encryptor, EMPTY_EXTRA_DATA_SUPPLIER); + } + + /** + * Create a new MetaStore with specified table name and extra data supplier. + * + * @param ddb Interface for accessing DynamoDB. + * @param tableName DynamoDB table name for this {@link MetaStore}. + * @param encryptor used to perform crypto operations on the record attributes + * @param extraDataSupplier provides extra data that should be stored along with the material. + */ + public MetaStore( + final AmazonDynamoDB ddb, + final String tableName, + final DynamoDBEncryptor encryptor, + final ExtraDataSupplier extraDataSupplier) { + this.ddb = checkNotNull(ddb, "ddb must not be null"); + this.tableName = checkNotNull(tableName, "tableName must not be null"); + this.encryptor = checkNotNull(encryptor, "encryptor must not be null"); + this.extraDataSupplier = checkNotNull(extraDataSupplier, "extraDataSupplier must not be null"); + + this.ddbCtx = + new EncryptionContext.Builder() + .withTableName(this.tableName) + .withHashKeyName(DEFAULT_HASH_KEY) + .withRangeKeyName(DEFAULT_RANGE_KEY) + .build(); + + final Map tmpExpected = new HashMap<>(); + tmpExpected.put(DEFAULT_HASH_KEY, new ExpectedAttributeValue().withExists(false)); + tmpExpected.put(DEFAULT_RANGE_KEY, new ExpectedAttributeValue().withExists(false)); + doesNotExist = Collections.unmodifiableMap(tmpExpected); + + this.doNotEncrypt = getSignedOnlyFields(extraDataSupplier); + } + + @Override + public EncryptionMaterialsProvider getProvider(final String materialName, final long version) { + Map item = getMaterialItem(materialName, version); + return decryptProvider(item); + } + + @Override + public EncryptionMaterialsProvider getOrCreate(final String materialName, final long nextId) { + final Map plaintext = createMaterialItem(materialName, nextId); + final Map ciphertext = conditionalPut(getEncryptedText(plaintext)); + return decryptProvider(ciphertext); + } + + @Override + public long getMaxVersion(final String materialName) { + final List> items = + ddb.query( new QueryRequest() - .withTableName(tableName) - .withConsistentRead(Boolean.TRUE) - .withKeyConditions( + .withTableName(tableName) + .withConsistentRead(Boolean.TRUE) + .withKeyConditions( Collections.singletonMap( - DEFAULT_HASH_KEY, - new Condition().withComparisonOperator( - ComparisonOperator.EQ).withAttributeValueList( - new AttributeValue().withS(materialName)))) - .withLimit(1).withScanIndexForward(false) - .withAttributesToGet(DEFAULT_RANGE_KEY)).getItems(); - if (items.isEmpty()) { - return -1L; - } else { - return Long.parseLong(items.get(0).get(DEFAULT_RANGE_KEY).getN()); - } + DEFAULT_HASH_KEY, + new Condition() + .withComparisonOperator(ComparisonOperator.EQ) + .withAttributeValueList(new AttributeValue().withS(materialName)))) + .withLimit(1) + .withScanIndexForward(false) + .withAttributesToGet(DEFAULT_RANGE_KEY)) + .getItems(); + if (items.isEmpty()) { + return -1L; + } else { + return Long.parseLong(items.get(0).get(DEFAULT_RANGE_KEY).getN()); } - - @Override - public long getVersionFromMaterialDescription(final Map description) { - final Matcher m = COMBINED_PATTERN.matcher(description.get(META_ID)); - if (m.matches()) { - return Long.parseLong(m.group(2)); - } else { - throw new IllegalArgumentException("No meta id found"); - } + } + + @Override + public long getVersionFromMaterialDescription(final Map description) { + final Matcher m = COMBINED_PATTERN.matcher(description.get(META_ID)); + if (m.matches()) { + return Long.parseLong(m.group(2)); + } else { + throw new IllegalArgumentException("No meta id found"); } - - /** - * This API retrieves the intermediate keys from the source region and replicates it in the target region. - * - * @param materialName material name of the encryption material. - * @param version version of the encryption material. - * @param targetMetaStore target MetaStore where the encryption material to be stored. - */ - public void replicate(final String materialName, final long version, final MetaStore targetMetaStore) { - try { - Map item = getMaterialItem(materialName, version); - final Map plainText = getPlainText(item); - final Map encryptedText = targetMetaStore.getEncryptedText(plainText); - final PutItemRequest put = new PutItemRequest().withTableName(targetMetaStore.tableName).withItem(encryptedText) - .withExpected(doesNotExist); - targetMetaStore.ddb.putItem(put); - } catch (ConditionalCheckFailedException e) { - //Item already present. - } - } - - private Map getMaterialItem(final String materialName, final long version) { - final Map ddbKey = new HashMap<>(); - ddbKey.put(DEFAULT_HASH_KEY, new AttributeValue().withS(materialName)); - ddbKey.put(DEFAULT_RANGE_KEY, new AttributeValue().withN(Long.toString(version))); - final Map item = ddbGet(ddbKey); - if (item == null || item.isEmpty()) { - throw new IndexOutOfBoundsException("No material found: " + materialName + "#" + version); - } - return item; - } - - /** - * Creates a DynamoDB Table with the correct properties to be used with a ProviderStore. - * - * @param ddb interface for accessing DynamoDB - * @param tableName name of table that stores the meta data of the material. - * @param provisionedThroughput required provisioned throughput of the this table. - * @return result of create table request. - */ - public static CreateTableResult createTable(final AmazonDynamoDB ddb, final String tableName, - final ProvisionedThroughput provisionedThroughput) { - return ddb.createTable(Arrays.asList(new AttributeDefinition(DEFAULT_HASH_KEY, - ScalarAttributeType.S), new AttributeDefinition(DEFAULT_RANGE_KEY, - ScalarAttributeType.N)), tableName, Arrays.asList(new KeySchemaElement( - DEFAULT_HASH_KEY, KeyType.HASH), new KeySchemaElement(DEFAULT_RANGE_KEY, - KeyType.RANGE)), provisionedThroughput); - - } - - /** - * Empty extra data supplier. This default class is intended to simplify the default - * implementation of {@link MetaStore}. - */ - private static class EmptyExtraDataSupplier implements ExtraDataSupplier { - @Override - public Map getAttributes(String materialName, long version) { - return Collections.emptyMap(); - } - - @Override - public Set getSignedOnlyFieldNames() { - return Collections.emptySet(); - } + } + + /** + * This API retrieves the intermediate keys from the source region and replicates it in the target + * region. + * + * @param materialName material name of the encryption material. + * @param version version of the encryption material. + * @param targetMetaStore target MetaStore where the encryption material to be stored. + */ + public void replicate( + final String materialName, final long version, final MetaStore targetMetaStore) { + try { + Map item = getMaterialItem(materialName, version); + final Map plainText = getPlainText(item); + final Map encryptedText = targetMetaStore.getEncryptedText(plainText); + final PutItemRequest put = + new PutItemRequest() + .withTableName(targetMetaStore.tableName) + .withItem(encryptedText) + .withExpected(doesNotExist); + targetMetaStore.ddb.putItem(put); + } catch (ConditionalCheckFailedException e) { + // Item already present. } - - /** - * Get a set of fields that must be signed but not encrypted. - * - * @param extraDataSupplier extra data supplier that is used to return sign only field names. - * @return fields that must be signed. - */ - private static Set getSignedOnlyFields(final ExtraDataSupplier extraDataSupplier) { - final Set signedOnlyFields = extraDataSupplier.getSignedOnlyFieldNames(); - for (final String signedOnlyField : signedOnlyFields) { - if (ENCRYPTED_FIELDS.contains(signedOnlyField)) { - throw new IllegalArgumentException(signedOnlyField + " must be encrypted"); - } - } - - // fields that should not be encrypted - final Set doNotEncryptFields = new HashSet<>(); - doNotEncryptFields.add(DEFAULT_HASH_KEY); - doNotEncryptFields.add(DEFAULT_RANGE_KEY); - doNotEncryptFields.addAll(signedOnlyFields); - return Collections.unmodifiableSet(doNotEncryptFields); + } + + private Map getMaterialItem( + final String materialName, final long version) { + final Map ddbKey = new HashMap<>(); + ddbKey.put(DEFAULT_HASH_KEY, new AttributeValue().withS(materialName)); + ddbKey.put(DEFAULT_RANGE_KEY, new AttributeValue().withN(Long.toString(version))); + final Map item = ddbGet(ddbKey); + if (item == null || item.isEmpty()) { + throw new IndexOutOfBoundsException("No material found: " + materialName + "#" + version); } - - private Map conditionalPut(final Map item) { - try { - final PutItemRequest put = new PutItemRequest().withTableName(tableName).withItem(item) - .withExpected(doesNotExist); - ddb.putItem(put); - return item; - } catch (final ConditionalCheckFailedException ex) { - final Map ddbKey = new HashMap<>(); - ddbKey.put(DEFAULT_HASH_KEY, item.get(DEFAULT_HASH_KEY)); - ddbKey.put(DEFAULT_RANGE_KEY, item.get(DEFAULT_RANGE_KEY)); - return ddbGet(ddbKey); - } + return item; + } + + /** + * Creates a DynamoDB Table with the correct properties to be used with a ProviderStore. + * + * @param ddb interface for accessing DynamoDB + * @param tableName name of table that stores the meta data of the material. + * @param provisionedThroughput required provisioned throughput of the this table. + * @return result of create table request. + */ + public static CreateTableResult createTable( + final AmazonDynamoDB ddb, + final String tableName, + final ProvisionedThroughput provisionedThroughput) { + return ddb.createTable( + Arrays.asList( + new AttributeDefinition(DEFAULT_HASH_KEY, ScalarAttributeType.S), + new AttributeDefinition(DEFAULT_RANGE_KEY, ScalarAttributeType.N)), + tableName, + Arrays.asList( + new KeySchemaElement(DEFAULT_HASH_KEY, KeyType.HASH), + new KeySchemaElement(DEFAULT_RANGE_KEY, KeyType.RANGE)), + provisionedThroughput); + } + + /** + * Empty extra data supplier. This default class is intended to simplify the default + * implementation of {@link MetaStore}. + */ + private static class EmptyExtraDataSupplier implements ExtraDataSupplier { + @Override + public Map getAttributes(String materialName, long version) { + return Collections.emptyMap(); } - private Map ddbGet(final Map ddbKey) { - return ddb.getItem( - new GetItemRequest().withTableName(tableName).withConsistentRead(true) - .withKey(ddbKey)).getItem(); + @Override + public Set getSignedOnlyFieldNames() { + return Collections.emptySet(); } - - /** - * Build an material item for a given material name and version with newly generated - * encryption and integrity keys. - * - * @param materialName material name. - * @param version version of the material. - * @return newly generated plaintext material item. - */ - private Map createMaterialItem(final String materialName, final long version) { - final SecretKeySpec encryptionKey = new SecretKeySpec(Utils.getRandom(32), DEFAULT_ENCRYPTION); - final SecretKeySpec integrityKey = new SecretKeySpec(Utils.getRandom(32), DEFAULT_INTEGRITY); - - final Map plaintext = new HashMap(); - plaintext.put(DEFAULT_HASH_KEY, new AttributeValue().withS(materialName)); - plaintext.put(DEFAULT_RANGE_KEY, new AttributeValue().withN(Long.toString(version))); - plaintext.put(MATERIAL_TYPE_VERSION, new AttributeValue().withS("0")); - plaintext.put(ENCRYPTION_KEY_FIELD, new AttributeValue().withB(ByteBuffer.wrap(encryptionKey.getEncoded()))); - plaintext.put(ENCRYPTION_ALGORITHM_FIELD, new AttributeValue().withS(encryptionKey.getAlgorithm())); - plaintext.put(INTEGRITY_KEY_FIELD, new AttributeValue().withB(ByteBuffer.wrap(integrityKey.getEncoded()))); - plaintext.put(INTEGRITY_ALGORITHM_FIELD, new AttributeValue().withS(integrityKey.getAlgorithm())); - plaintext.putAll(extraDataSupplier.getAttributes(materialName, version)); - - return plaintext; + } + + /** + * Get a set of fields that must be signed but not encrypted. + * + * @param extraDataSupplier extra data supplier that is used to return sign only field names. + * @return fields that must be signed. + */ + private static Set getSignedOnlyFields(final ExtraDataSupplier extraDataSupplier) { + final Set signedOnlyFields = extraDataSupplier.getSignedOnlyFieldNames(); + for (final String signedOnlyField : signedOnlyFields) { + if (ENCRYPTED_FIELDS.contains(signedOnlyField)) { + throw new IllegalArgumentException(signedOnlyField + " must be encrypted"); + } } - private EncryptionMaterialsProvider decryptProvider(final Map item) { - final Map plaintext = getPlainText(item); - - final String type = plaintext.get(MATERIAL_TYPE_VERSION).getS(); - final SecretKey encryptionKey; - final SecretKey integrityKey; - // This switch statement is to make future extensibility easier and more obvious - switch (type) { - case "0": // Only currently supported type - encryptionKey = new SecretKeySpec(plaintext.get(ENCRYPTION_KEY_FIELD).getB().array(), - plaintext.get(ENCRYPTION_ALGORITHM_FIELD).getS()); - integrityKey = new SecretKeySpec(plaintext.get(INTEGRITY_KEY_FIELD).getB().array(), plaintext - .get(INTEGRITY_ALGORITHM_FIELD).getS()); - break; - default: - throw new IllegalStateException("Unsupported material type: " + type); - } - return new WrappedMaterialsProvider(encryptionKey, encryptionKey, integrityKey, - buildDescription(plaintext)); + // fields that should not be encrypted + final Set doNotEncryptFields = new HashSet<>(); + doNotEncryptFields.add(DEFAULT_HASH_KEY); + doNotEncryptFields.add(DEFAULT_RANGE_KEY); + doNotEncryptFields.addAll(signedOnlyFields); + return Collections.unmodifiableSet(doNotEncryptFields); + } + + private Map conditionalPut(final Map item) { + try { + final PutItemRequest put = + new PutItemRequest().withTableName(tableName).withItem(item).withExpected(doesNotExist); + ddb.putItem(put); + return item; + } catch (final ConditionalCheckFailedException ex) { + final Map ddbKey = new HashMap<>(); + ddbKey.put(DEFAULT_HASH_KEY, item.get(DEFAULT_HASH_KEY)); + ddbKey.put(DEFAULT_RANGE_KEY, item.get(DEFAULT_RANGE_KEY)); + return ddbGet(ddbKey); } - - /** - * Decrypts attributes in the ciphertext item using {@link DynamoDBEncryptor}. - * except the attribute names specified in doNotEncrypt. - * @param ciphertext the ciphertext to be decrypted. - * @throws AmazonClientException when failed to decrypt material item. - * @return decrypted item. - */ - private Map getPlainText(final Map ciphertext) { - try { - return encryptor.decryptAllFieldsExcept(ciphertext, ddbCtx, doNotEncrypt); - } catch (final GeneralSecurityException e) { - throw new AmazonClientException(e); - } + } + + private Map ddbGet(final Map ddbKey) { + return ddb.getItem( + new GetItemRequest().withTableName(tableName).withConsistentRead(true).withKey(ddbKey)) + .getItem(); + } + + /** + * Build an material item for a given material name and version with newly generated encryption + * and integrity keys. + * + * @param materialName material name. + * @param version version of the material. + * @return newly generated plaintext material item. + */ + private Map createMaterialItem( + final String materialName, final long version) { + final SecretKeySpec encryptionKey = new SecretKeySpec(Utils.getRandom(32), DEFAULT_ENCRYPTION); + final SecretKeySpec integrityKey = new SecretKeySpec(Utils.getRandom(32), DEFAULT_INTEGRITY); + + final Map plaintext = new HashMap(); + plaintext.put(DEFAULT_HASH_KEY, new AttributeValue().withS(materialName)); + plaintext.put(DEFAULT_RANGE_KEY, new AttributeValue().withN(Long.toString(version))); + plaintext.put(MATERIAL_TYPE_VERSION, new AttributeValue().withS("0")); + plaintext.put( + ENCRYPTION_KEY_FIELD, + new AttributeValue().withB(ByteBuffer.wrap(encryptionKey.getEncoded()))); + plaintext.put( + ENCRYPTION_ALGORITHM_FIELD, new AttributeValue().withS(encryptionKey.getAlgorithm())); + plaintext.put( + INTEGRITY_KEY_FIELD, + new AttributeValue().withB(ByteBuffer.wrap(integrityKey.getEncoded()))); + plaintext.put( + INTEGRITY_ALGORITHM_FIELD, new AttributeValue().withS(integrityKey.getAlgorithm())); + plaintext.putAll(extraDataSupplier.getAttributes(materialName, version)); + + return plaintext; + } + + private EncryptionMaterialsProvider decryptProvider(final Map item) { + final Map plaintext = getPlainText(item); + + final String type = plaintext.get(MATERIAL_TYPE_VERSION).getS(); + final SecretKey encryptionKey; + final SecretKey integrityKey; + // This switch statement is to make future extensibility easier and more obvious + switch (type) { + case "0": // Only currently supported type + encryptionKey = + new SecretKeySpec( + plaintext.get(ENCRYPTION_KEY_FIELD).getB().array(), + plaintext.get(ENCRYPTION_ALGORITHM_FIELD).getS()); + integrityKey = + new SecretKeySpec( + plaintext.get(INTEGRITY_KEY_FIELD).getB().array(), + plaintext.get(INTEGRITY_ALGORITHM_FIELD).getS()); + break; + default: + throw new IllegalStateException("Unsupported material type: " + type); } - - /** - * Encrypts attributes in the plaintext item using {@link DynamoDBEncryptor}. - * except the attribute names specified in doNotEncrypt. - * - * @throws AmazonClientException when failed to encrypt material item. - * @param plaintext plaintext to be encrypted. - */ - private Map getEncryptedText(Map plaintext) { - try { - return encryptor.encryptAllFieldsExcept(plaintext, ddbCtx, doNotEncrypt); - } catch (final GeneralSecurityException e) { - throw new AmazonClientException(e); - } + return new WrappedMaterialsProvider( + encryptionKey, encryptionKey, integrityKey, buildDescription(plaintext)); + } + + /** + * Decrypts attributes in the ciphertext item using {@link DynamoDBEncryptor}. except the + * attribute names specified in doNotEncrypt. + * + * @param ciphertext the ciphertext to be decrypted. + * @throws AmazonClientException when failed to decrypt material item. + * @return decrypted item. + */ + private Map getPlainText(final Map ciphertext) { + try { + return encryptor.decryptAllFieldsExcept(ciphertext, ddbCtx, doNotEncrypt); + } catch (final GeneralSecurityException e) { + throw new AmazonClientException(e); } - - private Map buildDescription(final Map plaintext) { - return Collections.singletonMap(META_ID, plaintext.get(DEFAULT_HASH_KEY).getS() + "#" - + plaintext.get(DEFAULT_RANGE_KEY).getN()); + } + + /** + * Encrypts attributes in the plaintext item using {@link DynamoDBEncryptor}. except the attribute + * names specified in doNotEncrypt. + * + * @throws AmazonClientException when failed to encrypt material item. + * @param plaintext plaintext to be encrypted. + */ + private Map getEncryptedText(Map plaintext) { + try { + return encryptor.encryptAllFieldsExcept(plaintext, ddbCtx, doNotEncrypt); + } catch (final GeneralSecurityException e) { + throw new AmazonClientException(e); } - - private static V checkNotNull(final V ref, final String errMsg) { - if (ref == null) { - throw new NullPointerException(errMsg); - } else { - return ref; - } + } + + private Map buildDescription(final Map plaintext) { + return Collections.singletonMap( + META_ID, + plaintext.get(DEFAULT_HASH_KEY).getS() + "#" + plaintext.get(DEFAULT_RANGE_KEY).getN()); + } + + private static V checkNotNull(final V ref, final String errMsg) { + if (ref == null) { + throw new NullPointerException(errMsg); + } else { + return ref; } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/ProviderStore.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/ProviderStore.java index 95ee783d..5dcbe032 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/ProviderStore.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/ProviderStore.java @@ -1,84 +1,79 @@ /* * Copyright 2015 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.services.dynamodbv2.datamodeling.encryption.providers.store; -import java.util.Map; - import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; +import java.util.Map; /** * Provides a standard way to retrieve and optionally create {@link EncryptionMaterialsProvider}s * backed by some form of persistent storage. - * - * @author rubin * + * @author rubin */ public abstract class ProviderStore { - /** - * Returns the most recent provider with the specified name. If there are no providers with this - * name, it will create one with version 0. - */ - public EncryptionMaterialsProvider getProvider(final String materialName) { - final long currVersion = getMaxVersion(materialName); - if (currVersion >= 0) { - return getProvider(materialName, currVersion); - } else { - return getOrCreate(materialName, 0); - } + /** + * Returns the most recent provider with the specified name. If there are no providers with this + * name, it will create one with version 0. + */ + public EncryptionMaterialsProvider getProvider(final String materialName) { + final long currVersion = getMaxVersion(materialName); + if (currVersion >= 0) { + return getProvider(materialName, currVersion); + } else { + return getOrCreate(materialName, 0); } + } - /** - * Returns the provider with the specified name and version. - * - * @throws IndexOutOfBoundsException - * if {@code version} is not a valid version - */ - public abstract EncryptionMaterialsProvider getProvider(final String materialName, final long version); + /** + * Returns the provider with the specified name and version. + * + * @throws IndexOutOfBoundsException if {@code version} is not a valid version + */ + public abstract EncryptionMaterialsProvider getProvider( + final String materialName, final long version); - /** - * Creates a new provider with a version one greater than the current max version. If multiple - * clients attempt to create a provider with this same version simultaneously, they will - * properly coordinate and the result will be that a single provider is created and that all - * ProviderStores return the same one. - */ - public EncryptionMaterialsProvider newProvider(final String materialName) { - final long nextId = getMaxVersion(materialName) + 1; - return getOrCreate(materialName, nextId); - } + /** + * Creates a new provider with a version one greater than the current max version. If multiple + * clients attempt to create a provider with this same version simultaneously, they will properly + * coordinate and the result will be that a single provider is created and that all ProviderStores + * return the same one. + */ + public EncryptionMaterialsProvider newProvider(final String materialName) { + final long nextId = getMaxVersion(materialName) + 1; + return getOrCreate(materialName, nextId); + } - /** - * Returns the provider with the specified name and version and creates it if it doesn't exist. - * - * @throws UnsupportedOperationException - * if a new provider cannot be created - */ - public EncryptionMaterialsProvider getOrCreate(final String materialName, final long nextId) { - try { - return getProvider(materialName, nextId); - } catch (final IndexOutOfBoundsException ex) { - throw new UnsupportedOperationException("This ProviderStore does not support creation.", ex); - } + /** + * Returns the provider with the specified name and version and creates it if it doesn't exist. + * + * @throws UnsupportedOperationException if a new provider cannot be created + */ + public EncryptionMaterialsProvider getOrCreate(final String materialName, final long nextId) { + try { + return getProvider(materialName, nextId); + } catch (final IndexOutOfBoundsException ex) { + throw new UnsupportedOperationException("This ProviderStore does not support creation.", ex); } + } - /** - * Returns the maximum version number associated with {@code materialName}. If there are no - * versions, returns -1. - */ - public abstract long getMaxVersion(final String materialName); + /** + * Returns the maximum version number associated with {@code materialName}. If there are no + * versions, returns -1. + */ + public abstract long getMaxVersion(final String materialName); - /** - * Extracts the material version from {@code description}. - */ - public abstract long getVersionFromMaterialDescription(final Map description); + /** Extracts the material version from {@code description}. */ + public abstract long getVersionFromMaterialDescription(final Map description); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java index 150fa5bb..e9adbd28 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java @@ -15,67 +15,67 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; - import java.util.Map; import java.util.function.UnaryOperator; -/** - * Implementations of common operators for overriding the EncryptionContext - */ +/** Implementations of common operators for overriding the EncryptionContext */ public class EncryptionContextOperators { - // Prevent instantiation - private EncryptionContextOperators() { - } + // Prevent instantiation + private EncryptionContextOperators() {} - /** - * An operator for overriding EncryptionContext's table name for a specific DynamoDBEncryptor. If any table names or - * the encryption context itself is null, then it returns the original EncryptionContext. - * - * @param originalTableName the name of the table that should be overridden in the Encryption Context - * @param newTableName the table name that should be used in the Encryption Context - * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name - */ - public static UnaryOperator overrideEncryptionContextTableName( - String originalTableName, - String newTableName) { - return encryptionContext -> { - if (encryptionContext == null - || encryptionContext.getTableName() == null - || originalTableName == null - || newTableName == null) { - return encryptionContext; - } - if (originalTableName.equals(encryptionContext.getTableName())) { - return new EncryptionContext.Builder(encryptionContext).withTableName(newTableName).build(); - } else { - return encryptionContext; - } - }; - } + /** + * An operator for overriding EncryptionContext's table name for a specific DynamoDBEncryptor. If + * any table names or the encryption context itself is null, then it returns the original + * EncryptionContext. + * + * @param originalTableName the name of the table that should be overridden in the Encryption + * Context + * @param newTableName the table name that should be used in the Encryption Context + * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name + */ + public static UnaryOperator overrideEncryptionContextTableName( + String originalTableName, String newTableName) { + return encryptionContext -> { + if (encryptionContext == null + || encryptionContext.getTableName() == null + || originalTableName == null + || newTableName == null) { + return encryptionContext; + } + if (originalTableName.equals(encryptionContext.getTableName())) { + return new EncryptionContext.Builder(encryptionContext).withTableName(newTableName).build(); + } else { + return encryptionContext; + } + }; + } - /** - * An operator for mapping multiple table names in the Encryption Context to a new table name. If the table name for - * a given EncryptionContext is missing, then it returns the original EncryptionContext. Similarly, it returns the - * original EncryptionContext if the value it is overridden to is null, or if the original table name is null. - * - * @param tableNameOverrideMap a map specifying the names of tables that should be overridden, - * and the values to which they should be overridden. If the given table name - * corresponds to null, or isn't in the map, then the table name won't be overridden. - * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name - */ - public static UnaryOperator overrideEncryptionContextTableNameUsingMap( - Map tableNameOverrideMap) { - return encryptionContext -> { - if (tableNameOverrideMap == null || encryptionContext == null || encryptionContext.getTableName() == null) { - return encryptionContext; - } - String newTableName = tableNameOverrideMap.get(encryptionContext.getTableName()); - if (newTableName != null) { - return new EncryptionContext.Builder(encryptionContext).withTableName(newTableName).build(); - } else { - return encryptionContext; - } - }; - } + /** + * An operator for mapping multiple table names in the Encryption Context to a new table name. If + * the table name for a given EncryptionContext is missing, then it returns the original + * EncryptionContext. Similarly, it returns the original EncryptionContext if the value it is + * overridden to is null, or if the original table name is null. + * + * @param tableNameOverrideMap a map specifying the names of tables that should be overridden, and + * the values to which they should be overridden. If the given table name corresponds to null, + * or isn't in the map, then the table name won't be overridden. + * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name + */ + public static UnaryOperator overrideEncryptionContextTableNameUsingMap( + Map tableNameOverrideMap) { + return encryptionContext -> { + if (tableNameOverrideMap == null + || encryptionContext == null + || encryptionContext.getTableName() == null) { + return encryptionContext; + } + String newTableName = tableNameOverrideMap.get(encryptionContext.getTableName()); + if (newTableName != null) { + return new EncryptionContext.Builder(encryptionContext).withTableName(newTableName).build(); + } else { + return encryptionContext; + } + }; + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshaller.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshaller.java index 28a967ce..26622af4 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshaller.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshaller.java @@ -14,6 +14,7 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -27,279 +28,276 @@ import java.util.List; import java.util.Map; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; - -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public class AttributeValueMarshaller { - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final int TRUE_FLAG = 1; - private static final int FALSE_FLAG = 0; + private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final int TRUE_FLAG = 1; + private static final int FALSE_FLAG = 0; - private AttributeValueMarshaller() { - // Prevent instantiation - } + private AttributeValueMarshaller() { + // Prevent instantiation + } - /** - * Marshalls the data using a TLV (Tag-Length-Value) encoding. The tag may be 'b', 'n', 's', - * '?', '\0' to represent a ByteBuffer, Number, String, Boolean, or Null respectively. The tag - * may also be capitalized (for 'b', 'n', and 's',) to represent an array of that type. If an - * array is stored, then a four-byte big-endian integer is written representing the number of - * array elements. If a ByteBuffer is stored, the length of the buffer is stored as a four-byte - * big-endian integer and the buffer then copied directly. Both Numbers and Strings are treated - * identically and are stored as UTF8 encoded Unicode, proceeded by the length of the encoded - * string (in bytes) as a four-byte big-endian integer. Boolean is encoded as a single byte, 0 - * for false and 1 for true (and so has no Length parameter). The - * Null tag ('\0') takes neither a Length nor a Value parameter. - * - * The tags 'L' and 'M' are for the document types List and Map respectively. These are encoded - * recursively with the Length being the size of the collection. In the case of List, the value - * is a Length number of marshalled AttributeValues. If the case of Map, the value is a Length - * number of AttributeValue Pairs where the first must always have a String value. - * - * This implementation does not recognize loops. If an AttributeValue contains itself - * (even indirectly) this code will recurse infinitely. - * - * @param attributeValue - * @return the serialized AttributeValue - * @see java.io.DataInput - */ - public static ByteBuffer marshall(final AttributeValue attributeValue) { - try (ByteArrayOutputStream resultBytes = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(resultBytes);) { - marshall(attributeValue, out); - out.close(); - resultBytes.close(); - return ByteBuffer.wrap(resultBytes.toByteArray()); - } catch (final IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } + /** + * Marshalls the data using a TLV (Tag-Length-Value) encoding. The tag may be 'b', 'n', 's', '?', + * '\0' to represent a ByteBuffer, Number, String, Boolean, or Null respectively. The tag may also + * be capitalized (for 'b', 'n', and 's',) to represent an array of that type. If an array is + * stored, then a four-byte big-endian integer is written representing the number of array + * elements. If a ByteBuffer is stored, the length of the buffer is stored as a four-byte + * big-endian integer and the buffer then copied directly. Both Numbers and Strings are treated + * identically and are stored as UTF8 encoded Unicode, proceeded by the length of the encoded + * string (in bytes) as a four-byte big-endian integer. Boolean is encoded as a single byte, 0 for + * false and 1 for true (and so has no Length parameter). The Null tag + * ('\0') takes neither a Length nor a Value parameter. + * + *

The tags 'L' and 'M' are for the document types List and Map respectively. These are encoded + * recursively with the Length being the size of the collection. In the case of List, the value is + * a Length number of marshalled AttributeValues. If the case of Map, the value is a Length number + * of AttributeValue Pairs where the first must always have a String value. + * + *

This implementation does not recognize loops. If an AttributeValue contains itself + * (even indirectly) this code will recurse infinitely. + * + * @param attributeValue + * @return the serialized AttributeValue + * @see java.io.DataInput + */ + public static ByteBuffer marshall(final AttributeValue attributeValue) { + try (ByteArrayOutputStream resultBytes = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(resultBytes); ) { + marshall(attributeValue, out); + out.close(); + resultBytes.close(); + return ByteBuffer.wrap(resultBytes.toByteArray()); + } catch (final IOException ex) { + // Due to the objects in use, an IOException is not possible. + throw new RuntimeException("Unexpected exception", ex); } + } - private static void marshall(final AttributeValue attributeValue, final DataOutputStream out) - throws IOException { - - if (attributeValue.getB() != null) { - out.writeChar('b'); - writeBytes(attributeValue.getB(), out); - } else if (attributeValue.getBS() != null) { - out.writeChar('B'); - writeBytesList(attributeValue.getBS(), out); - } else if (attributeValue.getN() != null) { - out.writeChar('n'); - writeString(trimZeros(attributeValue.getN()), out); - } else if (attributeValue.getNS() != null) { - out.writeChar('N'); + private static void marshall(final AttributeValue attributeValue, final DataOutputStream out) + throws IOException { - final List ns = new ArrayList(attributeValue.getNS().size()); - for (final String n : attributeValue.getNS()) { - ns.add(trimZeros(n)); - } - writeStringList(ns, out); - } else if (attributeValue.getS() != null) { - out.writeChar('s'); - writeString(attributeValue.getS(), out); - } else if (attributeValue.getSS() != null) { - out.writeChar('S'); - writeStringList(attributeValue.getSS(), out); - } else if (attributeValue.getBOOL() != null) { - out.writeChar('?'); - out.writeByte((attributeValue.getBOOL() ? TRUE_FLAG : FALSE_FLAG)); - } else if (Boolean.TRUE.equals(attributeValue.getNULL())) { - out.writeChar('\0'); - } else if (attributeValue.getL() != null) { - final List l = attributeValue.getL(); - out.writeChar('L'); - out.writeInt(l.size()); - for (final AttributeValue attr : l) { - if (attr == null) { - throw new NullPointerException( - "Encountered null list entry value while marshalling attribute value " - + attributeValue); - } - marshall(attr, out); - } - } else if (attributeValue.getM() != null) { - final Map m = attributeValue.getM(); - final List mKeys = new ArrayList(m.keySet()); - Collections.sort(mKeys); - out.writeChar('M'); - out.writeInt(m.size()); - for (final String mKey : mKeys) { - marshall(new AttributeValue().withS(mKey), out); - - final AttributeValue mValue = m.get(mKey); - - if (mValue == null) { - throw new NullPointerException( - "Encountered null map value for key " - + mKey - + " while marshalling attribute value " - + attributeValue); - } - marshall(mValue, out); - } - } else { - throw new IllegalArgumentException("A seemingly empty AttributeValue is indicative of invalid input or potential errors"); - } - } + if (attributeValue.getB() != null) { + out.writeChar('b'); + writeBytes(attributeValue.getB(), out); + } else if (attributeValue.getBS() != null) { + out.writeChar('B'); + writeBytesList(attributeValue.getBS(), out); + } else if (attributeValue.getN() != null) { + out.writeChar('n'); + writeString(trimZeros(attributeValue.getN()), out); + } else if (attributeValue.getNS() != null) { + out.writeChar('N'); - /** - * @see #marshall(AttributeValue) - */ - public static AttributeValue unmarshall(final ByteBuffer plainText) { - try (final DataInputStream in = new DataInputStream( - new ByteBufferInputStream(plainText.asReadOnlyBuffer()))) { - return unmarshall(in); - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); + final List ns = new ArrayList(attributeValue.getNS().size()); + for (final String n : attributeValue.getNS()) { + ns.add(trimZeros(n)); + } + writeStringList(ns, out); + } else if (attributeValue.getS() != null) { + out.writeChar('s'); + writeString(attributeValue.getS(), out); + } else if (attributeValue.getSS() != null) { + out.writeChar('S'); + writeStringList(attributeValue.getSS(), out); + } else if (attributeValue.getBOOL() != null) { + out.writeChar('?'); + out.writeByte((attributeValue.getBOOL() ? TRUE_FLAG : FALSE_FLAG)); + } else if (Boolean.TRUE.equals(attributeValue.getNULL())) { + out.writeChar('\0'); + } else if (attributeValue.getL() != null) { + final List l = attributeValue.getL(); + out.writeChar('L'); + out.writeInt(l.size()); + for (final AttributeValue attr : l) { + if (attr == null) { + throw new NullPointerException( + "Encountered null list entry value while marshalling attribute value " + + attributeValue); } - } + marshall(attr, out); + } + } else if (attributeValue.getM() != null) { + final Map m = attributeValue.getM(); + final List mKeys = new ArrayList(m.keySet()); + Collections.sort(mKeys); + out.writeChar('M'); + out.writeInt(m.size()); + for (final String mKey : mKeys) { + marshall(new AttributeValue().withS(mKey), out); - private static AttributeValue unmarshall(final DataInputStream in) throws IOException { - char type = in.readChar(); - AttributeValue result = new AttributeValue(); - switch (type) { - case '\0': - result.setNULL(Boolean.TRUE); - break; - case 'b': - result.setB(readBytes(in)); - break; - case 'B': - result.setBS(readBytesList(in)); - break; - case 'n': - result.setN(readString(in)); - break; - case 'N': - result.setNS(readStringList(in)); - break; - case 's': - result.setS(readString(in)); - break; - case 'S': - result.setSS(readStringList(in)); - break; - case '?': - final byte boolValue = in.readByte(); + final AttributeValue mValue = m.get(mKey); - if (boolValue == TRUE_FLAG) { - result.setBOOL(Boolean.TRUE); - } else if (boolValue == FALSE_FLAG) { - result.setBOOL(Boolean.FALSE); - } else { - throw new IllegalArgumentException("Improperly formatted data"); - } - break; - case 'L': - final int lCount = in.readInt(); - final List l = new ArrayList(lCount); - for (int lIdx = 0; lIdx < lCount; lIdx++) { - l.add(unmarshall(in)); - } - result.setL(l); - break; - case 'M': - final int mCount = in.readInt(); - final Map m = new HashMap(); - for (int mIdx = 0; mIdx < mCount; mIdx++) { - final AttributeValue key = unmarshall(in); - if (key.getS() == null) { - throw new IllegalArgumentException("Improperly formatted data"); - } - AttributeValue value = unmarshall(in); - m.put(key.getS(), value); - } - result.setM(m); - break; - default: - throw new IllegalArgumentException("Unsupported data encoding"); + if (mValue == null) { + throw new NullPointerException( + "Encountered null map value for key " + + mKey + + " while marshalling attribute value " + + attributeValue); } - - return result; + marshall(mValue, out); + } + } else { + throw new IllegalArgumentException( + "A seemingly empty AttributeValue is indicative of invalid input or potential errors"); } + } - private static String trimZeros(final String n) { - BigDecimal number = new BigDecimal(n); - if (number.compareTo(BigDecimal.ZERO) == 0) { - return "0"; - } - return number.stripTrailingZeros().toPlainString(); + /** @see #marshall(AttributeValue) */ + public static AttributeValue unmarshall(final ByteBuffer plainText) { + try (final DataInputStream in = + new DataInputStream(new ByteBufferInputStream(plainText.asReadOnlyBuffer()))) { + return unmarshall(in); + } catch (IOException ex) { + // Due to the objects in use, an IOException is not possible. + throw new RuntimeException("Unexpected exception", ex); } + } + + private static AttributeValue unmarshall(final DataInputStream in) throws IOException { + char type = in.readChar(); + AttributeValue result = new AttributeValue(); + switch (type) { + case '\0': + result.setNULL(Boolean.TRUE); + break; + case 'b': + result.setB(readBytes(in)); + break; + case 'B': + result.setBS(readBytesList(in)); + break; + case 'n': + result.setN(readString(in)); + break; + case 'N': + result.setNS(readStringList(in)); + break; + case 's': + result.setS(readString(in)); + break; + case 'S': + result.setSS(readStringList(in)); + break; + case '?': + final byte boolValue = in.readByte(); - private static void writeStringList(List values, final DataOutputStream out) throws IOException { - final List sorted = new ArrayList(values); - Collections.sort(sorted); - out.writeInt(sorted.size()); - for (final String v : sorted) { - writeString(v, out); + if (boolValue == TRUE_FLAG) { + result.setBOOL(Boolean.TRUE); + } else if (boolValue == FALSE_FLAG) { + result.setBOOL(Boolean.FALSE); + } else { + throw new IllegalArgumentException("Improperly formatted data"); + } + break; + case 'L': + final int lCount = in.readInt(); + final List l = new ArrayList(lCount); + for (int lIdx = 0; lIdx < lCount; lIdx++) { + l.add(unmarshall(in)); + } + result.setL(l); + break; + case 'M': + final int mCount = in.readInt(); + final Map m = new HashMap(); + for (int mIdx = 0; mIdx < mCount; mIdx++) { + final AttributeValue key = unmarshall(in); + if (key.getS() == null) { + throw new IllegalArgumentException("Improperly formatted data"); + } + AttributeValue value = unmarshall(in); + m.put(key.getS(), value); } + result.setM(m); + break; + default: + throw new IllegalArgumentException("Unsupported data encoding"); } - private static List readStringList(final DataInputStream in) throws IOException, - IllegalArgumentException { - final int nCount = in.readInt(); - List ns = new ArrayList(nCount); - for (int nIdx = 0; nIdx < nCount; nIdx++) { - ns.add(readString(in)); - } - return ns; + return result; + } + + private static String trimZeros(final String n) { + BigDecimal number = new BigDecimal(n); + if (number.compareTo(BigDecimal.ZERO) == 0) { + return "0"; } + return number.stripTrailingZeros().toPlainString(); + } - private static void writeString(String value, final DataOutputStream out) throws IOException { - final byte[] bytes = value.getBytes(UTF8); - out.writeInt(bytes.length); - out.write(bytes); + private static void writeStringList(List values, final DataOutputStream out) + throws IOException { + final List sorted = new ArrayList(values); + Collections.sort(sorted); + out.writeInt(sorted.size()); + for (final String v : sorted) { + writeString(v, out); } + } - private static String readString(final DataInputStream in) throws IOException, - IllegalArgumentException { - byte[] bytes; - int length; - length = in.readInt(); - bytes = new byte[length]; - if(in.read(bytes) != length) { - throw new IllegalArgumentException("Improperly formatted data"); - } - String tmp = new String(bytes, UTF8); - return tmp; + private static List readStringList(final DataInputStream in) + throws IOException, IllegalArgumentException { + final int nCount = in.readInt(); + List ns = new ArrayList(nCount); + for (int nIdx = 0; nIdx < nCount; nIdx++) { + ns.add(readString(in)); } + return ns; + } - private static void writeBytesList(List values, final DataOutputStream out) throws IOException { - final List sorted = new ArrayList(values); - Collections.sort(sorted); - out.writeInt(sorted.size()); - for (final ByteBuffer v : sorted) { - writeBytes(v, out); - } + private static void writeString(String value, final DataOutputStream out) throws IOException { + final byte[] bytes = value.getBytes(UTF8); + out.writeInt(bytes.length); + out.write(bytes); + } + + private static String readString(final DataInputStream in) + throws IOException, IllegalArgumentException { + byte[] bytes; + int length; + length = in.readInt(); + bytes = new byte[length]; + if (in.read(bytes) != length) { + throw new IllegalArgumentException("Improperly formatted data"); } + String tmp = new String(bytes, UTF8); + return tmp; + } - private static List readBytesList(final DataInputStream in) throws IOException { - final int bCount = in.readInt(); - List bs = new ArrayList(bCount); - for (int bIdx = 0; bIdx < bCount; bIdx++) { - bs.add(readBytes(in)); - } - return bs; + private static void writeBytesList(List values, final DataOutputStream out) + throws IOException { + final List sorted = new ArrayList(values); + Collections.sort(sorted); + out.writeInt(sorted.size()); + for (final ByteBuffer v : sorted) { + writeBytes(v, out); } + } - private static void writeBytes(ByteBuffer value, final DataOutputStream out) throws IOException { - value = value.asReadOnlyBuffer(); - value.rewind(); - out.writeInt(value.remaining()); - while (value.hasRemaining()) { - out.writeByte(value.get()); - } + private static List readBytesList(final DataInputStream in) throws IOException { + final int bCount = in.readInt(); + List bs = new ArrayList(bCount); + for (int bIdx = 0; bIdx < bCount; bIdx++) { + bs.add(readBytes(in)); } + return bs; + } - private static ByteBuffer readBytes(final DataInputStream in) throws IOException { - final int length = in.readInt(); - final byte[] buf = new byte[length]; - in.readFully(buf); - return ByteBuffer.wrap(buf); + private static void writeBytes(ByteBuffer value, final DataOutputStream out) throws IOException { + value = value.asReadOnlyBuffer(); + value.rewind(); + out.writeInt(value.remaining()); + while (value.hasRemaining()) { + out.writeByte(value.get()); } + } + + private static ByteBuffer readBytes(final DataInputStream in) throws IOException { + final int length = in.readInt(); + final byte[] buf = new byte[length]; + in.readFully(buf); + return ByteBuffer.wrap(buf); + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64.java index e8f62955..19998711 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64.java @@ -17,33 +17,31 @@ import java.util.Base64.Decoder; import java.util.Base64.Encoder; -/** - * A class for decoding Base64 strings and encoding bytes as Base64 strings. - */ +/** A class for decoding Base64 strings and encoding bytes as Base64 strings. */ public class Base64 { - private static final Decoder DECODER = java.util.Base64.getMimeDecoder(); - private static final Encoder ENCODER = java.util.Base64.getEncoder(); + private static final Decoder DECODER = java.util.Base64.getMimeDecoder(); + private static final Encoder ENCODER = java.util.Base64.getEncoder(); - private Base64() { } + private Base64() {} - /** - * Encode the bytes as a Base64 string. - *

- * See the Basic encoder in {@link java.util.Base64} - */ - public static String encodeToString(byte[] bytes) { - return ENCODER.encodeToString(bytes); - } + /** + * Encode the bytes as a Base64 string. + * + *

See the Basic encoder in {@link java.util.Base64} + */ + public static String encodeToString(byte[] bytes) { + return ENCODER.encodeToString(bytes); + } - /** - * Decode the Base64 string as bytes, ignoring illegal characters. - *

- * See the Mime Decoder in {@link java.util.Base64} - */ - public static byte[] decode(String str) { - if(str == null) { - return null; - } - return DECODER.decode(str); + /** + * Decode the Base64 string as bytes, ignoring illegal characters. + * + *

See the Mime Decoder in {@link java.util.Base64} + */ + public static byte[] decode(String str) { + if (str == null) { + return null; } + return DECODER.decode(str); + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStream.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStream.java index f9df5254..e842821a 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStream.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStream.java @@ -17,40 +17,38 @@ import java.io.InputStream; import java.nio.ByteBuffer; -/** - * @author Greg Rubin - */ +/** @author Greg Rubin */ public class ByteBufferInputStream extends InputStream { - private final ByteBuffer buffer; + private final ByteBuffer buffer; - public ByteBufferInputStream(ByteBuffer buffer) { - this.buffer = buffer; - } + public ByteBufferInputStream(ByteBuffer buffer) { + this.buffer = buffer; + } - @Override - public int read() { - if (buffer.hasRemaining()) { - int tmp = buffer.get(); - if (tmp < 0) { - tmp += 256; - } - return tmp; - } else { - return -1; - } - } - - @Override - public int read(byte[] b, int off, int len) { - if (available() < len) { - len = available(); - } - buffer.get(b, off, len); - return len; + @Override + public int read() { + if (buffer.hasRemaining()) { + int tmp = buffer.get(); + if (tmp < 0) { + tmp += 256; + } + return tmp; + } else { + return -1; } - - @Override - public int available() { - return buffer.remaining(); + } + + @Override + public int read(byte[] b, int off, int len) { + if (available() < len) { + len = available(); } + buffer.get(b, off, len); + return len; + } + + @Override + public int available() { + return buffer.remaining(); + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Hkdf.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Hkdf.java index fc4d0c19..6aa766fd 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Hkdf.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Hkdf.java @@ -14,304 +14,265 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; +import com.amazonaws.util.StringUtils; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.util.Arrays; - import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; -import com.amazonaws.util.StringUtils; - /** * HMAC-based Key Derivation Function. * * @see RFC 5869 */ public final class Hkdf { - private static final byte[] EMPTY_ARRAY = new byte[0]; - private final String algorithm; - private final Provider provider; + private static final byte[] EMPTY_ARRAY = new byte[0]; + private final String algorithm; + private final Provider provider; - private SecretKey prk = null; + private SecretKey prk = null; - /** - * Returns an Hkdf object using the specified algorithm. - * - * @param algorithm - * the standard name of the requested MAC algorithm. See the Mac - * section in the Java Cryptography Architecture Standard Algorithm Name - * Documentation for information about standard algorithm - * names. - * @return the new Hkdf object - * @throws NoSuchAlgorithmException - * if no Provider supports a MacSpi implementation for the - * specified algorithm. - */ - public static Hkdf getInstance(final String algorithm) - throws NoSuchAlgorithmException { - // Constructed specifically to sanity-test arguments. - Mac mac = Mac.getInstance(algorithm); - return new Hkdf(algorithm, mac.getProvider()); - } + /** + * Returns an Hkdf object using the specified algorithm. + * + * @param algorithm the standard name of the requested MAC algorithm. See the Mac section in the + * + * Java Cryptography Architecture Standard Algorithm Name Documentation for information + * about standard algorithm names. + * @return the new Hkdf object + * @throws NoSuchAlgorithmException if no Provider supports a MacSpi implementation for the + * specified algorithm. + */ + public static Hkdf getInstance(final String algorithm) throws NoSuchAlgorithmException { + // Constructed specifically to sanity-test arguments. + Mac mac = Mac.getInstance(algorithm); + return new Hkdf(algorithm, mac.getProvider()); + } - /** - * Returns an Hkdf object using the specified algorithm. - * - * @param algorithm - * the standard name of the requested MAC algorithm. See the Mac - * section in the Java Cryptography Architecture Standard Algorithm Name - * Documentation for information about standard algorithm - * names. - * @param provider - * the name of the provider - * @return the new Hkdf object - * @throws NoSuchAlgorithmException - * if a MacSpi implementation for the specified algorithm is not - * available from the specified provider. - * @throws NoSuchProviderException - * if the specified provider is not registered in the security - * provider list. - */ - public static Hkdf getInstance(final String algorithm, final String provider) - throws NoSuchAlgorithmException, NoSuchProviderException { - // Constructed specifically to sanity-test arguments. - Mac mac = Mac.getInstance(algorithm, provider); - return new Hkdf(algorithm, mac.getProvider()); - } + /** + * Returns an Hkdf object using the specified algorithm. + * + * @param algorithm the standard name of the requested MAC algorithm. See the Mac section in the + * + * Java Cryptography Architecture Standard Algorithm Name Documentation for information + * about standard algorithm names. + * @param provider the name of the provider + * @return the new Hkdf object + * @throws NoSuchAlgorithmException if a MacSpi implementation for the specified algorithm is not + * available from the specified provider. + * @throws NoSuchProviderException if the specified provider is not registered in the security + * provider list. + */ + public static Hkdf getInstance(final String algorithm, final String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + // Constructed specifically to sanity-test arguments. + Mac mac = Mac.getInstance(algorithm, provider); + return new Hkdf(algorithm, mac.getProvider()); + } - /** - * Returns an Hkdf object using the specified algorithm. - * - * @param algorithm - * the standard name of the requested MAC algorithm. See the Mac - * section in the Java Cryptography Architecture Standard Algorithm Name - * Documentation for information about standard algorithm - * names. - * @param provider - * the provider - * @return the new Hkdf object - * @throws NoSuchAlgorithmException - * if a MacSpi implementation for the specified algorithm is not - * available from the specified provider. - */ - public static Hkdf getInstance(final String algorithm, - final Provider provider) throws NoSuchAlgorithmException { - // Constructed specifically to sanity-test arguments. - Mac mac = Mac.getInstance(algorithm, provider); - return new Hkdf(algorithm, mac.getProvider()); - } + /** + * Returns an Hkdf object using the specified algorithm. + * + * @param algorithm the standard name of the requested MAC algorithm. See the Mac section in the + * + * Java Cryptography Architecture Standard Algorithm Name Documentation for information + * about standard algorithm names. + * @param provider the provider + * @return the new Hkdf object + * @throws NoSuchAlgorithmException if a MacSpi implementation for the specified algorithm is not + * available from the specified provider. + */ + public static Hkdf getInstance(final String algorithm, final Provider provider) + throws NoSuchAlgorithmException { + // Constructed specifically to sanity-test arguments. + Mac mac = Mac.getInstance(algorithm, provider); + return new Hkdf(algorithm, mac.getProvider()); + } + + /** + * Initializes this Hkdf with input keying material. A default salt of HashLen zeros will be used + * (where HashLen is the length of the return value of the supplied algorithm). + * + * @param ikm the Input Keying Material + */ + public void init(final byte[] ikm) { + init(ikm, null); + } - /** - * Initializes this Hkdf with input keying material. A default salt of - * HashLen zeros will be used (where HashLen is the length of the return - * value of the supplied algorithm). - * - * @param ikm - * the Input Keying Material - */ - public void init(final byte[] ikm) { - init(ikm, null); + /** + * Initializes this Hkdf with input keying material and a salt. If + * salt is null or of length 0, then a default salt of HashLen zeros will be + * used (where HashLen is the length of the return value of the supplied algorithm). + * + * @param salt the salt used for key extraction (optional) + * @param ikm the Input Keying Material + */ + public void init(final byte[] ikm, final byte[] salt) { + byte[] realSalt = (salt == null) ? EMPTY_ARRAY : salt.clone(); + byte[] rawKeyMaterial = EMPTY_ARRAY; + try { + Mac extractionMac = Mac.getInstance(algorithm, provider); + if (realSalt.length == 0) { + realSalt = new byte[extractionMac.getMacLength()]; + Arrays.fill(realSalt, (byte) 0); + } + extractionMac.init(new SecretKeySpec(realSalt, algorithm)); + rawKeyMaterial = extractionMac.doFinal(ikm); + SecretKeySpec key = new SecretKeySpec(rawKeyMaterial, algorithm); + Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array + unsafeInitWithoutKeyExtraction(key); + } catch (GeneralSecurityException e) { + // We've already checked all of the parameters so no exceptions + // should be possible here. + throw new RuntimeException("Unexpected exception", e); + } finally { + Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array } + } - /** - * Initializes this Hkdf with input keying material and a salt. If - * salt is null or of length 0, then a default salt of - * HashLen zeros will be used (where HashLen is the length of the return - * value of the supplied algorithm). - * - * @param salt - * the salt used for key extraction (optional) - * @param ikm - * the Input Keying Material - */ - public void init(final byte[] ikm, final byte[] salt) { - byte[] realSalt = (salt == null) ? EMPTY_ARRAY : salt.clone(); - byte[] rawKeyMaterial = EMPTY_ARRAY; - try { - Mac extractionMac = Mac.getInstance(algorithm, provider); - if (realSalt.length == 0) { - realSalt = new byte[extractionMac.getMacLength()]; - Arrays.fill(realSalt, (byte) 0); - } - extractionMac.init(new SecretKeySpec(realSalt, algorithm)); - rawKeyMaterial = extractionMac.doFinal(ikm); - SecretKeySpec key = new SecretKeySpec(rawKeyMaterial, algorithm); - Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array - unsafeInitWithoutKeyExtraction(key); - } catch (GeneralSecurityException e) { - // We've already checked all of the parameters so no exceptions - // should be possible here. - throw new RuntimeException("Unexpected exception", e); - } finally { - Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array - } + /** + * Initializes this Hkdf to use the provided key directly for creation of new keys. If + * rawKey is not securely generated and uniformly distributed over the total key-space, + * then this will result in an insecure key derivation function (KDF). DO NOT USE THIS UNLESS + * YOU ARE ABSOLUTELY POSITIVE THIS IS THE CORRECT THING TO DO. + * + * @param rawKey the pseudorandom key directly used to derive keys + * @throws InvalidKeyException if the algorithm for rawKey does not match the + * algorithm this Hkdf was created with + */ + public void unsafeInitWithoutKeyExtraction(final SecretKey rawKey) throws InvalidKeyException { + if (!rawKey.getAlgorithm().equals(algorithm)) { + throw new InvalidKeyException( + "Algorithm for the provided key must match the algorithm for this Hkdf. Expected " + + algorithm + + " but found " + + rawKey.getAlgorithm()); } - /** - * Initializes this Hkdf to use the provided key directly for creation of - * new keys. If rawKey is not securely generated and uniformly - * distributed over the total key-space, then this will result in an - * insecure key derivation function (KDF). DO NOT USE THIS UNLESS YOU - * ARE ABSOLUTELY POSITIVE THIS IS THE CORRECT THING TO DO. - * - * @param rawKey - * the pseudorandom key directly used to derive keys - * @throws InvalidKeyException - * if the algorithm for rawKey does not match the - * algorithm this Hkdf was created with - */ - public void unsafeInitWithoutKeyExtraction(final SecretKey rawKey) - throws InvalidKeyException { - if (!rawKey.getAlgorithm().equals(algorithm)) { - throw new InvalidKeyException( - "Algorithm for the provided key must match the algorithm for this Hkdf. Expected " + - algorithm + " but found " + rawKey.getAlgorithm()); - } + this.prk = rawKey; + } - this.prk = rawKey; + private Hkdf(final String algorithm, final Provider provider) { + if (!algorithm.startsWith("Hmac")) { + throw new IllegalArgumentException( + "Invalid algorithm " + algorithm + ". Hkdf may only be used with Hmac algorithms."); } + this.algorithm = algorithm; + this.provider = provider; + } - private Hkdf(final String algorithm, final Provider provider) { - if (!algorithm.startsWith("Hmac")) { - throw new IllegalArgumentException("Invalid algorithm " + algorithm - + ". Hkdf may only be used with Hmac algorithms."); - } - this.algorithm = algorithm; - this.provider = provider; + /** + * Returns a pseudorandom key of length bytes. + * + * @param info optional context and application specific information (can be a zero-length + * string). This will be treated as UTF-8. + * @param length the length of the output key in bytes + * @return a pseudorandom key of length bytes. + * @throws IllegalStateException if this object has not been initialized + */ + public byte[] deriveKey(final String info, final int length) throws IllegalStateException { + return deriveKey((info != null ? info.getBytes(StringUtils.UTF8) : null), length); + } + + /** + * Returns a pseudorandom key of length bytes. + * + * @param info optional context and application specific information (can be a zero-length array). + * @param length the length of the output key in bytes + * @return a pseudorandom key of length bytes. + * @throws IllegalStateException if this object has not been initialized + */ + public byte[] deriveKey(final byte[] info, final int length) throws IllegalStateException { + byte[] result = new byte[length]; + try { + deriveKey(info, length, result, 0); + } catch (ShortBufferException ex) { + // This exception is impossible as we ensure the buffer is long + // enough + throw new RuntimeException(ex); } + return result; + } - /** - * Returns a pseudorandom key of length bytes. - * - * @param info - * optional context and application specific information (can be - * a zero-length string). This will be treated as UTF-8. - * @param length - * the length of the output key in bytes - * @return a pseudorandom key of length bytes. - * @throws IllegalStateException - * if this object has not been initialized - */ - public byte[] deriveKey(final String info, final int length) throws IllegalStateException { - return deriveKey((info != null ? info.getBytes(StringUtils.UTF8) : null), length); + /** + * Derives a pseudorandom key of length bytes and stores the result in output + * . + * + * @param info optional context and application specific information (can be a zero-length array). + * @param length the length of the output key in bytes + * @param output the buffer where the pseudorandom key will be stored + * @param offset the offset in output where the key will be stored + * @throws ShortBufferException if the given output buffer is too small to hold the result + * @throws IllegalStateException if this object has not been initialized + */ + public void deriveKey(final byte[] info, final int length, final byte[] output, final int offset) + throws ShortBufferException, IllegalStateException { + assertInitialized(); + if (length < 0) { + throw new IllegalArgumentException("Length must be a non-negative value."); + } + if (output.length < offset + length) { + throw new ShortBufferException(); } + Mac mac = createMac(); - /** - * Returns a pseudorandom key of length bytes. - * - * @param info - * optional context and application specific information (can be - * a zero-length array). - * @param length - * the length of the output key in bytes - * @return a pseudorandom key of length bytes. - * @throws IllegalStateException - * if this object has not been initialized - */ - public byte[] deriveKey(final byte[] info, final int length) throws IllegalStateException { - byte[] result = new byte[length]; - try { - deriveKey(info, length, result, 0); - } catch (ShortBufferException ex) { - // This exception is impossible as we ensure the buffer is long - // enough - throw new RuntimeException(ex); - } - return result; + if (length > 255 * mac.getMacLength()) { + throw new IllegalArgumentException( + "Requested keys may not be longer than 255 times the underlying HMAC length."); } - /** - * Derives a pseudorandom key of length bytes and stores the - * result in output. - * - * @param info - * optional context and application specific information (can be - * a zero-length array). - * @param length - * the length of the output key in bytes - * @param output - * the buffer where the pseudorandom key will be stored - * @param offset - * the offset in output where the key will be stored - * @throws ShortBufferException - * if the given output buffer is too small to hold the result - * @throws IllegalStateException - * if this object has not been initialized - */ - public void deriveKey(final byte[] info, final int length, - final byte[] output, final int offset) throws ShortBufferException, - IllegalStateException { - assertInitialized(); - if (length < 0) { - throw new IllegalArgumentException("Length must be a non-negative value."); - } - if (output.length < offset + length) { - throw new ShortBufferException(); - } - Mac mac = createMac(); + byte[] t = EMPTY_ARRAY; + try { + int loc = 0; + byte i = 1; + while (loc < length) { + mac.update(t); + mac.update(info); + mac.update(i); + t = mac.doFinal(); - if (length > 255 * mac.getMacLength()) { - throw new IllegalArgumentException( - "Requested keys may not be longer than 255 times the underlying HMAC length."); + for (int x = 0; x < t.length && loc < length; x++, loc++) { + output[loc] = t[x]; } - byte[] t = EMPTY_ARRAY; - try { - int loc = 0; - byte i = 1; - while (loc < length) { - mac.update(t); - mac.update(info); - mac.update(i); - t = mac.doFinal(); - - for (int x = 0; x < t.length && loc < length; x++, loc++) { - output[loc] = t[x]; - } - - i++; - } - } finally { - Arrays.fill(t, (byte) 0); // Zeroize temporary array - } + i++; + } + } finally { + Arrays.fill(t, (byte) 0); // Zeroize temporary array } + } - private Mac createMac() { - try { - Mac mac = Mac.getInstance(algorithm, provider); - mac.init(prk); - return mac; - } catch (NoSuchAlgorithmException ex) { - // We've already validated that this algorithm is correct. - throw new RuntimeException(ex); - } catch (InvalidKeyException ex) { - // We've already validated that this key is correct. - throw new RuntimeException(ex); - } + private Mac createMac() { + try { + Mac mac = Mac.getInstance(algorithm, provider); + mac.init(prk); + return mac; + } catch (NoSuchAlgorithmException ex) { + // We've already validated that this algorithm is correct. + throw new RuntimeException(ex); + } catch (InvalidKeyException ex) { + // We've already validated that this key is correct. + throw new RuntimeException(ex); } + } - /** - * Throws an IllegalStateException if this object has not been - * initialized. - * - * @throws IllegalStateException - * if this object has not been initialized - */ - private void assertInitialized() throws IllegalStateException { - if (prk == null) { - throw new IllegalStateException("Hkdf has not been initialized"); - } + /** + * Throws an IllegalStateException if this object has not been initialized. + * + * @throws IllegalStateException if this object has not been initialized + */ + private void assertInitialized() throws IllegalStateException { + if (prk == null) { + throw new IllegalStateException("Hkdf has not been initialized"); } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCache.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCache.java index e4efb3b6..b43c66ad 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCache.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCache.java @@ -3,7 +3,6 @@ package com.amazonaws.services.dynamodbv2.datamodeling.internal; import com.amazonaws.annotation.ThreadSafe; - import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -12,84 +11,70 @@ /** * A bounded cache that has a LRU eviction policy when the cache is full. * - * @param - * value type + * @param value type */ @ThreadSafe public final class LRUCache { - /** - * Used for the internal cache. - */ - private final Map map; + /** Used for the internal cache. */ + private final Map map; - /** - * Maximum size of the cache. - */ - private final int maxSize; + /** Maximum size of the cache. */ + private final int maxSize; - /** - * @param maxSize - * the maximum number of entries of the cache - */ - public LRUCache(final int maxSize) { - if (maxSize < 1) { - throw new IllegalArgumentException("maxSize " + maxSize + " must be at least 1"); - } - this.maxSize = maxSize; - map = Collections.synchronizedMap(new LRUHashMap<>(maxSize)); + /** @param maxSize the maximum number of entries of the cache */ + public LRUCache(final int maxSize) { + if (maxSize < 1) { + throw new IllegalArgumentException("maxSize " + maxSize + " must be at least 1"); } + this.maxSize = maxSize; + map = Collections.synchronizedMap(new LRUHashMap<>(maxSize)); + } - /** - * Adds an entry to the cache, evicting the earliest entry if necessary. - */ - public T add(final String key, final T value) { - return map.put(key, value); - } + /** Adds an entry to the cache, evicting the earliest entry if necessary. */ + public T add(final String key, final T value) { + return map.put(key, value); + } - /** Returns the value of the given key; or null of no such entry exists. */ - public T get(final String key) { - return map.get(key); - } + /** Returns the value of the given key; or null of no such entry exists. */ + public T get(final String key) { + return map.get(key); + } - /** - * Returns the current size of the cache. - */ - public int size() { - return map.size(); - } + /** Returns the current size of the cache. */ + public int size() { + return map.size(); + } - /** - * Returns the maximum size of the cache. - */ - public int getMaxSize() { - return maxSize; - } + /** Returns the maximum size of the cache. */ + public int getMaxSize() { + return maxSize; + } - public void clear() { - map.clear(); - } + public void clear() { + map.clear(); + } - public T remove(String key) { - return map.remove(key); - } + public T remove(String key) { + return map.remove(key); + } - @Override - public String toString() { - return map.toString(); - } + @Override + public String toString() { + return map.toString(); + } - @SuppressWarnings("serial") - private static class LRUHashMap extends LinkedHashMap { - private final int maxSize; + @SuppressWarnings("serial") + private static class LRUHashMap extends LinkedHashMap { + private final int maxSize; - private LRUHashMap(final int maxSize) { - super(10, 0.75F, true); - this.maxSize = maxSize; - } + private LRUHashMap(final int maxSize) { + super(10, 0.75F, true); + this.maxSize = maxSize; + } - @Override - protected boolean removeEldestEntry(final Entry eldest) { - return size() > maxSize; - } + @Override + protected boolean removeEldestEntry(final Entry eldest) { + return size() > maxSize; } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/MsClock.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/MsClock.java index f0f77b48..c1e464e0 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/MsClock.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/MsClock.java @@ -14,7 +14,7 @@ package com.amazonaws.services.dynamodbv2.datamodeling.internal; interface MsClock { - MsClock WALLCLOCK = System::nanoTime; + MsClock WALLCLOCK = System::nanoTime; - public long timestampNano(); + public long timestampNano(); } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCache.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCache.java index 18a3af1f..a8a611ba 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCache.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCache.java @@ -2,258 +2,241 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import com.amazonaws.annotation.ThreadSafe; +import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.checkNotNull; +import com.amazonaws.annotation.ThreadSafe; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; -import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.checkNotNull; - /** - * A cache, backed by an LRUCache, that uses a loader to calculate values on cache miss - * or expired TTL. + * A cache, backed by an LRUCache, that uses a loader to calculate values on cache miss or expired + * TTL. * - * Note that this cache does not proactively evict expired entries, - * however will immediately evict entries discovered to be expired on load. + *

Note that this cache does not proactively evict expired entries, however will immediately + * evict entries discovered to be expired on load. * - * @param - * value type + * @param value type */ @ThreadSafe public final class TTLCache { - /** - * Used for the internal cache. - */ - private final LRUCache> cache; + /** Used for the internal cache. */ + private final LRUCache> cache; - /** - * Time to live for entries in the cache. - */ - private final long ttlInNanos; + /** Time to live for entries in the cache. */ + private final long ttlInNanos; - /** - * Used for loading new values into the cache on cache miss or expiration. - */ - private final EntryLoader defaultLoader; + /** Used for loading new values into the cache on cache miss or expiration. */ + private final EntryLoader defaultLoader; - // Mockable time source, to allow us to test TTL behavior. - // package access for tests - MsClock clock = MsClock.WALLCLOCK; + // Mockable time source, to allow us to test TTL behavior. + // package access for tests + MsClock clock = MsClock.WALLCLOCK; - private static final long TTL_GRACE_IN_NANO = TimeUnit.MILLISECONDS.toNanos(500); + private static final long TTL_GRACE_IN_NANO = TimeUnit.MILLISECONDS.toNanos(500); - /** - * @param maxSize - * the maximum number of entries of the cache - * @param ttlInMillis - * the time to live value for entries of the cache, in milliseconds - */ - public TTLCache(final int maxSize, final long ttlInMillis, final EntryLoader loader) { - if (maxSize < 1) { - throw new IllegalArgumentException("maxSize " + maxSize + " must be at least 1"); - } - if (ttlInMillis < 1) { - throw new IllegalArgumentException("ttlInMillis " + maxSize + " must be at least 1"); - } - this.ttlInNanos = TimeUnit.MILLISECONDS.toNanos(ttlInMillis); - this.cache = new LRUCache<>(maxSize); - this.defaultLoader = checkNotNull(loader, "loader must not be null"); + /** + * @param maxSize the maximum number of entries of the cache + * @param ttlInMillis the time to live value for entries of the cache, in milliseconds + */ + public TTLCache(final int maxSize, final long ttlInMillis, final EntryLoader loader) { + if (maxSize < 1) { + throw new IllegalArgumentException("maxSize " + maxSize + " must be at least 1"); } - - /** - * Uses the default loader to calculate the value at key and insert it into the cache, - * if it doesn't already exist or is expired according to the TTL. - * - * This immediately evicts entries past the TTL such that a load failure results - * in the removal of the entry. - * - * Entries that are not expired according to the TTL are returned without recalculating the value. - * - * Within a grace period past the TTL, the cache may either return the cached value without recalculating - * or use the loader to recalculate the value. This is implemented such that, in a multi-threaded environment, - * only one thread per cache key uses the loader to recalculate the value at one time. - * - * @param key - * The cache key to load the value at - * @return - * The value of the given value (already existing or re-calculated). - */ - public T load(final String key) { - return load(key, defaultLoader::load); + if (ttlInMillis < 1) { + throw new IllegalArgumentException("ttlInMillis " + maxSize + " must be at least 1"); } - - /** - * Uses the inputted function to calculate the value at key and insert it into the cache, - * if it doesn't already exist or is expired according to the TTL. - * - * This immediately evicts entries past the TTL such that a load failure results - * in the removal of the entry. - * - * Entries that are not expired according to the TTL are returned without recalculating the value. - * - * Within a grace period past the TTL, the cache may either return the cached value without recalculating - * or use the loader to recalculate the value. This is implemented such that, in a multi-threaded environment, - * only one thread per cache key uses the loader to recalculate the value at one time. - * - * Returns the value of the given key (already existing or re-calculated). - * - * @param key - * The cache key to load the value at - * @param f - * The function to use to load the value, given key as input - * @return - * The value of the given value (already existing or re-calculated). - */ - public T load(final String key, Function f) { - final LockedState ls = cache.get(key); - - if (ls == null) { - // The entry doesn't exist yet, so load a new one. - return loadNewEntryIfAbsent(key, f); - } else if (clock.timestampNano() - ls.getState().lastUpdatedNano > ttlInNanos + TTL_GRACE_IN_NANO) { - // The data has expired past the grace period. - // Evict the old entry and load a new entry. - cache.remove(key); - return loadNewEntryIfAbsent(key, f); - } else if (clock.timestampNano() - ls.getState().lastUpdatedNano <= ttlInNanos) { - // The data hasn't expired. Return as-is from the cache. - return ls.getState().data; - } else if (!ls.tryLock()) { - // We are in the TTL grace period. If we couldn't grab the lock, then some other - // thread is currently loading the new value. Because we are in the grace period, - // use the cached data instead of waiting for the lock. - return ls.getState().data; - } - - // We are in the grace period and have acquired a lock. - // Update the cache with the value determined by the loading function. - try { - T loadedData = f.apply(key); - ls.update(loadedData, clock.timestampNano()); - return ls.getState().data; - } finally { - ls.unlock(); - } + this.ttlInNanos = TimeUnit.MILLISECONDS.toNanos(ttlInMillis); + this.cache = new LRUCache<>(maxSize); + this.defaultLoader = checkNotNull(loader, "loader must not be null"); + } + + /** + * Uses the default loader to calculate the value at key and insert it into the cache, if it + * doesn't already exist or is expired according to the TTL. + * + *

This immediately evicts entries past the TTL such that a load failure results in the removal + * of the entry. + * + *

Entries that are not expired according to the TTL are returned without recalculating the + * value. + * + *

Within a grace period past the TTL, the cache may either return the cached value without + * recalculating or use the loader to recalculate the value. This is implemented such that, in a + * multi-threaded environment, only one thread per cache key uses the loader to recalculate the + * value at one time. + * + * @param key The cache key to load the value at + * @return The value of the given value (already existing or re-calculated). + */ + public T load(final String key) { + return load(key, defaultLoader::load); + } + + /** + * Uses the inputted function to calculate the value at key and insert it into the cache, if it + * doesn't already exist or is expired according to the TTL. + * + *

This immediately evicts entries past the TTL such that a load failure results in the removal + * of the entry. + * + *

Entries that are not expired according to the TTL are returned without recalculating the + * value. + * + *

Within a grace period past the TTL, the cache may either return the cached value without + * recalculating or use the loader to recalculate the value. This is implemented such that, in a + * multi-threaded environment, only one thread per cache key uses the loader to recalculate the + * value at one time. + * + *

Returns the value of the given key (already existing or re-calculated). + * + * @param key The cache key to load the value at + * @param f The function to use to load the value, given key as input + * @return The value of the given value (already existing or re-calculated). + */ + public T load(final String key, Function f) { + final LockedState ls = cache.get(key); + + if (ls == null) { + // The entry doesn't exist yet, so load a new one. + return loadNewEntryIfAbsent(key, f); + } else if (clock.timestampNano() - ls.getState().lastUpdatedNano + > ttlInNanos + TTL_GRACE_IN_NANO) { + // The data has expired past the grace period. + // Evict the old entry and load a new entry. + cache.remove(key); + return loadNewEntryIfAbsent(key, f); + } else if (clock.timestampNano() - ls.getState().lastUpdatedNano <= ttlInNanos) { + // The data hasn't expired. Return as-is from the cache. + return ls.getState().data; + } else if (!ls.tryLock()) { + // We are in the TTL grace period. If we couldn't grab the lock, then some other + // thread is currently loading the new value. Because we are in the grace period, + // use the cached data instead of waiting for the lock. + return ls.getState().data; } - // Synchronously calculate the value for a new entry in the cache if it doesn't already exist. - // Otherwise return the cached value. - // It is important that this is the only place where we use the loader for a new entry, - // given that we don't have the entry yet to lock on. - // This ensures that the loading function is only called once if multiple threads - // attempt to add a new entry for the same key at the same time. - private synchronized T loadNewEntryIfAbsent(final String key, Function f) { - // If the entry already exists in the cache, return it - final LockedState cachedState = cache.get(key); - if (cachedState != null) { - return cachedState.getState().data; - } - - // Otherwise, load the data and create a new entry - T loadedData = f.apply(key); - LockedState ls = new LockedState<>(loadedData, clock.timestampNano()); - cache.add(key, ls); - return loadedData; + // We are in the grace period and have acquired a lock. + // Update the cache with the value determined by the loading function. + try { + T loadedData = f.apply(key); + ls.update(loadedData, clock.timestampNano()); + return ls.getState().data; + } finally { + ls.unlock(); } - - /** - * Put a new entry in the cache. - * Returns the value previously at that key in the cache, - * or null if the entry previously didn't exist or - * is expired. - */ - public synchronized T put(final String key, final T value) { - LockedState ls = new LockedState<>(value, clock.timestampNano()); - LockedState oldLockedState = cache.add(key, ls); - if (oldLockedState == null || clock.timestampNano() - oldLockedState.getState().lastUpdatedNano > ttlInNanos + TTL_GRACE_IN_NANO) { - return null; - } - return oldLockedState.getState().data; + } + + // Synchronously calculate the value for a new entry in the cache if it doesn't already exist. + // Otherwise return the cached value. + // It is important that this is the only place where we use the loader for a new entry, + // given that we don't have the entry yet to lock on. + // This ensures that the loading function is only called once if multiple threads + // attempt to add a new entry for the same key at the same time. + private synchronized T loadNewEntryIfAbsent(final String key, Function f) { + // If the entry already exists in the cache, return it + final LockedState cachedState = cache.get(key); + if (cachedState != null) { + return cachedState.getState().data; } - /** - * Get when the entry at this key was last updated. - * Returns 0 if the entry doesn't exist at key. - */ - public long getLastUpdated(String key) { - LockedState ls = cache.get(key); - if (ls == null) { - return 0; - } - return ls.getState().lastUpdatedNano; + // Otherwise, load the data and create a new entry + T loadedData = f.apply(key); + LockedState ls = new LockedState<>(loadedData, clock.timestampNano()); + cache.add(key, ls); + return loadedData; + } + + /** + * Put a new entry in the cache. Returns the value previously at that key in the cache, or null if + * the entry previously didn't exist or is expired. + */ + public synchronized T put(final String key, final T value) { + LockedState ls = new LockedState<>(value, clock.timestampNano()); + LockedState oldLockedState = cache.add(key, ls); + if (oldLockedState == null + || clock.timestampNano() - oldLockedState.getState().lastUpdatedNano + > ttlInNanos + TTL_GRACE_IN_NANO) { + return null; } - - /** - * Returns the current size of the cache. - */ - public int size() { - return cache.size(); + return oldLockedState.getState().data; + } + + /** + * Get when the entry at this key was last updated. Returns 0 if the entry doesn't exist at key. + */ + public long getLastUpdated(String key) { + LockedState ls = cache.get(key); + if (ls == null) { + return 0; } - - /** - * Returns the maximum size of the cache. - */ - public int getMaxSize() { - return cache.getMaxSize(); + return ls.getState().lastUpdatedNano; + } + + /** Returns the current size of the cache. */ + public int size() { + return cache.size(); + } + + /** Returns the maximum size of the cache. */ + public int getMaxSize() { + return cache.getMaxSize(); + } + + /** Clears all entries from the cache. */ + public void clear() { + cache.clear(); + } + + @Override + public String toString() { + return cache.toString(); + } + + public interface EntryLoader { + T load(String entryKey); + } + + // An object which stores a state alongside a lock, + // and performs updates to that state atomically. + // The state may only be updated if the lock is acquired by the current thread. + private static class LockedState { + private final ReentrantLock lock = new ReentrantLock(true); + private final AtomicReference> state; + + public LockedState(T data, long createTimeNano) { + state = new AtomicReference<>(new State<>(data, createTimeNano)); } - /** - * Clears all entries from the cache. - */ - public void clear() { - cache.clear(); + public State getState() { + return state.get(); } - @Override - public String toString() { - return cache.toString(); + public void unlock() { + lock.unlock(); } - public interface EntryLoader { - T load(String entryKey); + public boolean tryLock() { + return lock.tryLock(); } - // An object which stores a state alongside a lock, - // and performs updates to that state atomically. - // The state may only be updated if the lock is acquired by the current thread. - private static class LockedState { - private final ReentrantLock lock = new ReentrantLock(true); - private final AtomicReference> state; - - public LockedState(T data, long createTimeNano) { - state = new AtomicReference<>(new State<>(data, createTimeNano)); - } - - public State getState() { - return state.get(); - } - - public void unlock() { - lock.unlock(); - } - - public boolean tryLock() { - return lock.tryLock(); - } - - public void update(T data, long createTimeNano) { - if (!lock.isHeldByCurrentThread()) { - throw new IllegalStateException("Lock not held by current thread"); - } - state.set(new State<>(data, createTimeNano)); - } + public void update(T data, long createTimeNano) { + if (!lock.isHeldByCurrentThread()) { + throw new IllegalStateException("Lock not held by current thread"); + } + state.set(new State<>(data, createTimeNano)); } + } - // An object that holds some data and the time at which this object was created - private static class State { - public final T data; - public final long lastUpdatedNano; + // An object that holds some data and the time at which this object was created + private static class State { + public final T data; + public final long lastUpdatedNano; - public State(T data, long lastUpdatedNano) { - this.data = data; - this.lastUpdatedNano = lastUpdatedNano; - } + public State(T data, long lastUpdatedNano) { + this.data = data; + this.lastUpdatedNano = lastUpdatedNano; } + } } diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java index 86e6604a..06abd3dc 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java @@ -19,48 +19,48 @@ import java.util.Properties; public class Utils { - private static final ThreadLocal RND = new ThreadLocal() { + private static final ThreadLocal RND = + new ThreadLocal() { @Override protected SecureRandom initialValue() { - final SecureRandom result = new SecureRandom(); - result.nextBoolean(); // Force seeding - return result; + final SecureRandom result = new SecureRandom(); + result.nextBoolean(); // Force seeding + return result; } - }; + }; - private Utils() { - // Prevent instantiation - } + private Utils() { + // Prevent instantiation + } - public static SecureRandom getRng() { - return RND.get(); - } + public static SecureRandom getRng() { + return RND.get(); + } - public static byte[] getRandom(int len) { - final byte[] result = new byte[len]; - getRng().nextBytes(result); - return result; - } + public static byte[] getRandom(int len) { + final byte[] result = new byte[len]; + getRng().nextBytes(result); + return result; + } - public static V checkNotNull(final V ref, final String errMsg) { - if (ref == null) { - throw new NullPointerException(errMsg); - } else { - return ref; - } + public static V checkNotNull(final V ref, final String errMsg) { + if (ref == null) { + throw new NullPointerException(errMsg); + } else { + return ref; } + } - /* - * Loads the version of the library - */ - public static String loadVersion() { - try { - final Properties properties = new Properties(); - properties.load(ClassLoader.getSystemResourceAsStream("project.properties")); - return properties.getProperty("version"); - } catch (final IOException ex) { - return "unknown"; - } + /* + * Loads the version of the library + */ + public static String loadVersion() { + try { + final Properties properties = new Properties(); + properties.load(ClassLoader.getSystemResourceAsStream("project.properties")); + return properties.getProperty("version"); + } catch (final IOException ex) { + return "unknown"; } - + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptorTest.java index fd9abb32..0d29e81b 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptorTest.java @@ -14,6 +14,12 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.AttributeTransformer.Parameters; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.SymmetricStaticProvider; @@ -35,14 +41,6 @@ import com.amazonaws.services.dynamodbv2.testing.types.UntouchedWithNewAttribute; import com.amazonaws.services.dynamodbv2.testing.types.UntouchedWithUnknownAttributeAnnotation; import com.amazonaws.services.dynamodbv2.testing.types.UntouchedWithUnknownAttributeAnnotationWithNewAttribute; -import org.testng.Assert; -import org.testng.AssertJUnit; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.nio.ByteBuffer; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -53,567 +51,631 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class AttributeEncryptorTest { - private static final String RANGE_KEY = "rangeKey"; - private static final String HASH_KEY = "hashKey"; - private static final String TABLE_NAME = "TableName"; - private static SecretKey encryptionKey; - private static SecretKey macKey; - - private EncryptionMaterialsProvider prov; - private AttributeEncryptor encryptor; - private Map attribs; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() throws Exception { - prov = new SymmetricStaticProvider(encryptionKey, macKey, - Collections.emptyMap()); - encryptor = new AttributeEncryptor(prov); - - attribs = new HashMap(); - attribs.put("intValue", new AttributeValue().withN("123")); - attribs.put("stringValue", new AttributeValue().withS("Hello world!")); - attribs.put("byteArrayValue", - new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}))); - attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); - attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); - attribs.put(HASH_KEY, new AttributeValue().withN("5")); - attribs.put(RANGE_KEY, new AttributeValue().withN("7")); - attribs.put("version", new AttributeValue().withN("0")); - } - - @Test - public void testUnaffected() { - Parameters params = FakeParameters.getInstance(Untouched.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertEquals(attribs, encryptedAttributes); - } - - @Test - public void fullEncryption() { - Parameters params = FakeParameters.getInstance(BaseClass.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(BaseClass.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); - assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").getS()); - assertNotNull(encryptedAttributes.get("stringValue").getB()); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void rejectsPartialUpdate() { - Parameters params = FakeParameters.getInstance(BaseClass.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY, true); - encryptor.transform(params); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void fullEncryptionBadSignature() { - Parameters params = FakeParameters.getInstance(BaseClass.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get(HASH_KEY).setN("666"); - params = FakeParameters.getInstance(BaseClass.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void badVersionNumber() { - Parameters params = FakeParameters.getInstance(BaseClass.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - ByteBuffer materialDescription = encryptedAttributes.get( - encryptor.getEncryptor().getMaterialDescriptionFieldName()).getB(); - byte[] rawArray = materialDescription.array(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getEncryptor().getMaterialDescriptionFieldName(), - new AttributeValue().withB(ByteBuffer.wrap(rawArray))); - params = FakeParameters.getInstance(BaseClass.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test - public void signedOnly() { - Parameters params = FakeParameters.getInstance(SignOnly.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(SignOnly.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); - assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() { - prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); - encryptor = new AttributeEncryptor(prov); - Parameters params = FakeParameters.getInstance(SignOnly.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(SignOnly.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); - assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void signedOnlyBadSignature() { - Parameters params = FakeParameters.getInstance(SignOnly.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get(HASH_KEY).setN("666"); - params = FakeParameters.getInstance(SignOnly.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void signedOnlyNoSignature() { - Parameters params = FakeParameters.getInstance(SignOnly.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getEncryptor().getSignatureFieldName()); - encryptor.untransform(params); - } - - @Test - public void RsaSignedOnly() throws NoSuchAlgorithmException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new AttributeEncryptor(new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap())); - - Parameters params = FakeParameters.getInstance(SignOnly.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(SignOnly.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); - assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void RsaSignedOnlyBadSignature() throws NoSuchAlgorithmException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new AttributeEncryptor(new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap())); - Parameters params = FakeParameters.getInstance(SignOnly.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get(HASH_KEY).setN("666"); - params = FakeParameters.getInstance(SignOnly.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test - public void mixed() { - Parameters params = FakeParameters.getInstance(Mixed.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(Mixed.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); - assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure StringSet has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringSet")); - assertNull(encryptedAttributes.get("stringSet").getSS()); - assertNotNull(encryptedAttributes.get("stringSet").getB()); - - // Test those not encrypted - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - assertAttrEquals(attribs.get("intValue"), encryptedAttributes.get("intValue")); - - // intValue is not signed, make sure we can modify it and still decrypt - encryptedAttributes.get("intValue").setN("666"); - - params = FakeParameters.getInstance(Mixed.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void mixedBadSignature() { - Parameters params = FakeParameters.getInstance(Mixed.class, attribs, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("stringValue").setS("666"); - params = FakeParameters.getInstance(Mixed.class, encryptedAttributes, null, TABLE_NAME, - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void tableNameRespected() { - Parameters params = FakeParameters.getInstance(BaseClass.class, attribs, null, "firstTable", - HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(BaseClass.class, encryptedAttributes, null, "secondTable", - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test - public void tableNameOverridden() { - Parameters params = FakeParameters.getInstance(TableOverride.class, attribs, null, "firstTable", - HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - params = FakeParameters.getInstance(TableOverride.class, encryptedAttributes, null, "secondTable", - HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testUnknownAttributeFails() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foobar")); - Parameters params = FakeParameters.getInstance( - BaseClassWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - params = FakeParameters.getInstance(BaseClass.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - encryptor.untransform(params); - } - - @Test - public void testUntouchedWithUnknownAttribute() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foobar")); - Parameters params = FakeParameters.getInstance( - UntouchedWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.match(attributes)); - params = FakeParameters.getInstance(Untouched.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - } - - @Test - public void testUntouchedWithUnknownAttributeAnnotation() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foobar")); - Parameters params = FakeParameters.getInstance( - UntouchedWithUnknownAttributeAnnotationWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.match(attributes)); - params = FakeParameters.getInstance( - UntouchedWithUnknownAttributeAnnotation.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - } - - @Test - public void testSignOnlyWithUnknownAttributeAnnotation() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foobar")); - Parameters params = FakeParameters.getInstance( - SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(new AttributeValue().withS("foobar"), encryptedAttributes.get("newAttribute")); - params = FakeParameters.getInstance( - SignOnlyWithUnknownAttributeAnnotation.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testSignOnlyWithUnknownAttributeAnnotationBadSignature() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foo")); - Parameters params = FakeParameters.getInstance( - SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(new AttributeValue().withS("foo"), encryptedAttributes.get("newAttribute")); - params = FakeParameters.getInstance( - SignOnlyWithUnknownAttributeAnnotation.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - encryptedAttributes.get("newAttribute").setS("bar"); - encryptor.untransform(params); - } - - @Test - public void testEncryptWithUnknownAttributeAnnotation() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foo")); - Parameters params = FakeParameters.getInstance( - BaseClassWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - params = FakeParameters.getInstance( - BaseClassWithUnknownAttributeAnnotation.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testEncryptWithUnknownAttributeAnnotationBadSignature() { - Map attributes = new HashMap<>(attribs); - attributes.put("newAttribute", new AttributeValue().withS("foo")); - Parameters params = FakeParameters.getInstance( - BaseClassWithNewAttribute.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - params = FakeParameters.getInstance( - BaseClassWithUnknownAttributeAnnotation.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - encryptedAttributes.get("newAttribute").setB(ByteBuffer.allocate(0)); - encryptor.untransform(params); - } - - @Test - public void testEncryptWithFieldLevelDoNotEncryptAnnotation() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotEncryptField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotEncryptField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - } - - @Test - public void testEncryptWithFieldLevelDoNotEncryptAnnotationWithChangedDoNotTouchSuperClass() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotEncryptField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotEncryptField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - - // Change a DoNotTouch value on Mixed super class - encryptedAttributes.put("intValue", new AttributeValue().withN("666")); - Map decryptedAttributes = encryptor.untransform(params); - - Map modifiedAttributes = new HashMap<>(attributes); - modifiedAttributes.put("intValue", new AttributeValue().withN("666")); - - assertThat(decryptedAttributes, AttrMatcher.match(modifiedAttributes)); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testEncryptWithFieldLevelDoNotEncryptAnnotationBadSignature() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotEncryptField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotEncryptField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - encryptedAttributes.put("value", new AttributeValue().withN("200")); - encryptor.untransform(params); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testEncryptWithFieldLevelDoNotEncryptAnnotationBadSignatureSuperClass() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotEncryptField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotEncryptField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - - // Change DoNotEncrypt value on Mixed super class - encryptedAttributes.put("doubleValue", new AttributeValue().withN("200")); - encryptor.untransform(params); - } - - @Test - public void testEncryptWithFieldLevelDoNotTouchAnnotation() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotTouchField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotTouchField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - } - - @Test - public void testEncryptWithFieldLevelDoNotTouchAnnotationChangeValue() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotTouchField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotTouchField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - encryptedAttributes.put("value", new AttributeValue().withN("200")); - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(new AttributeValue().withN("200"), decryptedAttributes.get("value")); - - // Change a DoNotTouch value on Mixed super class - encryptedAttributes.put("intValue", new AttributeValue().withN("666")); - decryptedAttributes = encryptor.untransform(params); - - Map modifiedAttributes = new HashMap<>(attributes); - modifiedAttributes.put("intValue", new AttributeValue().withN("666")); - modifiedAttributes.put("value", new AttributeValue().withN("200")); - - assertThat(decryptedAttributes, AttrMatcher.match(modifiedAttributes)); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testEncryptWithFieldLevelDoNotTouchAnnotationBadSignatureSuperClass() { - Map attributes = new HashMap<>(attribs); - attributes.put("value", new AttributeValue().withN("100")); - Parameters params = FakeParameters.getInstance( - DoNotTouchField.class, attributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - Map encryptedAttributes = encryptor.transform(params); - assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); - assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); - params = FakeParameters.getInstance( - DoNotTouchField.class, encryptedAttributes, null, - TABLE_NAME, HASH_KEY, RANGE_KEY); - - Map decryptedAttributes = encryptor.untransform(params); - assertThat(decryptedAttributes, AttrMatcher.match(attributes)); - - // Change DoNotEncrypt value on Mixed super class - encryptedAttributes.put("doubleValue", new AttributeValue().withN("200")); - encryptor.untransform(params); - } - - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - Assert.assertEquals(o1.getB(), o2.getB()); - assertSetsEqual(o1.getBS(), o2.getBS()); - Assert.assertEquals(o1.getN(), o2.getN()); - assertSetsEqual(o1.getNS(), o2.getNS()); - Assert.assertEquals(o1.getS(), o2.getS()); - assertSetsEqual(o1.getSS(), o2.getSS()); - } - - private void assertSetsEqual(Collection c1, Collection c2) { - Assert.assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - Assert.assertEquals(s1, s2); - } - } + private static final String RANGE_KEY = "rangeKey"; + private static final String HASH_KEY = "hashKey"; + private static final String TABLE_NAME = "TableName"; + private static SecretKey encryptionKey; + private static SecretKey macKey; + + private EncryptionMaterialsProvider prov; + private AttributeEncryptor encryptor; + private Map attribs; + + @BeforeClass + public static void setUpClass() throws Exception { + KeyGenerator aesGen = KeyGenerator.getInstance("AES"); + aesGen.init(128, Utils.getRng()); + encryptionKey = aesGen.generateKey(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, Utils.getRng()); + macKey = macGen.generateKey(); + } + + @BeforeMethod + public void setUp() throws Exception { + prov = + new SymmetricStaticProvider(encryptionKey, macKey, Collections.emptyMap()); + encryptor = new AttributeEncryptor(prov); + + attribs = new HashMap(); + attribs.put("intValue", new AttributeValue().withN("123")); + attribs.put("stringValue", new AttributeValue().withS("Hello world!")); + attribs.put( + "byteArrayValue", + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5}))); + attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); + attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); + attribs.put(HASH_KEY, new AttributeValue().withN("5")); + attribs.put(RANGE_KEY, new AttributeValue().withN("7")); + attribs.put("version", new AttributeValue().withN("0")); + } + + @Test + public void testUnaffected() { + Parameters params = + FakeParameters.getInstance(Untouched.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertEquals(attribs, encryptedAttributes); + } + + @Test + public void fullEncryption() { + Parameters params = + FakeParameters.getInstance(BaseClass.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + BaseClass.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); + assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has been encrypted (we'll assume the others are correct as well) + assertTrue(encryptedAttributes.containsKey("stringValue")); + assertNull(encryptedAttributes.get("stringValue").getS()); + assertNotNull(encryptedAttributes.get("stringValue").getB()); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void rejectsPartialUpdate() { + Parameters params = + FakeParameters.getInstance( + BaseClass.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY, true); + encryptor.transform(params); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void fullEncryptionBadSignature() { + Parameters params = + FakeParameters.getInstance(BaseClass.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get(HASH_KEY).setN("666"); + params = + FakeParameters.getInstance( + BaseClass.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void badVersionNumber() { + Parameters params = + FakeParameters.getInstance(BaseClass.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + ByteBuffer materialDescription = + encryptedAttributes.get(encryptor.getEncryptor().getMaterialDescriptionFieldName()).getB(); + byte[] rawArray = materialDescription.array(); + assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. + rawArray[0] = 100; + encryptedAttributes.put( + encryptor.getEncryptor().getMaterialDescriptionFieldName(), + new AttributeValue().withB(ByteBuffer.wrap(rawArray))); + params = + FakeParameters.getInstance( + BaseClass.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test + public void signedOnly() { + Parameters params = + FakeParameters.getInstance(SignOnly.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + SignOnly.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); + assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test + public void signedOnlyNullCryptoKey() { + prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); + encryptor = new AttributeEncryptor(prov); + Parameters params = + FakeParameters.getInstance(SignOnly.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + SignOnly.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); + assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void signedOnlyBadSignature() { + Parameters params = + FakeParameters.getInstance(SignOnly.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get(HASH_KEY).setN("666"); + params = + FakeParameters.getInstance( + SignOnly.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void signedOnlyNoSignature() { + Parameters params = + FakeParameters.getInstance(SignOnly.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.remove(encryptor.getEncryptor().getSignatureFieldName()); + encryptor.untransform(params); + } + + @Test + public void RsaSignedOnly() throws NoSuchAlgorithmException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + new AttributeEncryptor( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap())); + + Parameters params = + FakeParameters.getInstance(SignOnly.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + SignOnly.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); + assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void RsaSignedOnlyBadSignature() throws NoSuchAlgorithmException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + new AttributeEncryptor( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap())); + Parameters params = + FakeParameters.getInstance(SignOnly.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get(HASH_KEY).setN("666"); + params = + FakeParameters.getInstance( + SignOnly.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test + public void mixed() { + Parameters params = + FakeParameters.getInstance(Mixed.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + Mixed.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get(HASH_KEY), encryptedAttributes.get(HASH_KEY)); + assertAttrEquals(attribs.get(RANGE_KEY), encryptedAttributes.get(RANGE_KEY)); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure StringSet has been encrypted (we'll assume the others are correct as well) + assertTrue(encryptedAttributes.containsKey("stringSet")); + assertNull(encryptedAttributes.get("stringSet").getSS()); + assertNotNull(encryptedAttributes.get("stringSet").getB()); + + // Test those not encrypted + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + assertAttrEquals(attribs.get("intValue"), encryptedAttributes.get("intValue")); + + // intValue is not signed, make sure we can modify it and still decrypt + encryptedAttributes.get("intValue").setN("666"); + + params = + FakeParameters.getInstance( + Mixed.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void mixedBadSignature() { + Parameters params = + FakeParameters.getInstance(Mixed.class, attribs, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("stringValue").setS("666"); + params = + FakeParameters.getInstance( + Mixed.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void tableNameRespected() { + Parameters params = + FakeParameters.getInstance( + BaseClass.class, attribs, null, "firstTable", HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + BaseClass.class, encryptedAttributes, null, "secondTable", HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test + public void tableNameOverridden() { + Parameters params = + FakeParameters.getInstance( + TableOverride.class, attribs, null, "firstTable", HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + params = + FakeParameters.getInstance( + TableOverride.class, encryptedAttributes, null, "secondTable", HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testUnknownAttributeFails() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foobar")); + Parameters params = + FakeParameters.getInstance( + BaseClassWithNewAttribute.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + params = + FakeParameters.getInstance( + BaseClass.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptor.untransform(params); + } + + @Test + public void testUntouchedWithUnknownAttribute() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foobar")); + Parameters params = + FakeParameters.getInstance( + UntouchedWithNewAttribute.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.match(attributes)); + params = + FakeParameters.getInstance( + Untouched.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + } + + @Test + public void testUntouchedWithUnknownAttributeAnnotation() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foobar")); + Parameters params = + FakeParameters.getInstance( + UntouchedWithUnknownAttributeAnnotationWithNewAttribute.class, + attributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.match(attributes)); + params = + FakeParameters.getInstance( + UntouchedWithUnknownAttributeAnnotation.class, + encryptedAttributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + } + + @Test + public void testSignOnlyWithUnknownAttributeAnnotation() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foobar")); + Parameters params = + FakeParameters.getInstance( + SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.class, + attributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(new AttributeValue().withS("foobar"), encryptedAttributes.get("newAttribute")); + params = + FakeParameters.getInstance( + SignOnlyWithUnknownAttributeAnnotation.class, + encryptedAttributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testSignOnlyWithUnknownAttributeAnnotationBadSignature() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foo")); + Parameters params = + FakeParameters.getInstance( + SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.class, + attributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(new AttributeValue().withS("foo"), encryptedAttributes.get("newAttribute")); + params = + FakeParameters.getInstance( + SignOnlyWithUnknownAttributeAnnotation.class, + encryptedAttributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + encryptedAttributes.get("newAttribute").setS("bar"); + encryptor.untransform(params); + } + + @Test + public void testEncryptWithUnknownAttributeAnnotation() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foo")); + Parameters params = + FakeParameters.getInstance( + BaseClassWithNewAttribute.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + params = + FakeParameters.getInstance( + BaseClassWithUnknownAttributeAnnotation.class, + encryptedAttributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testEncryptWithUnknownAttributeAnnotationBadSignature() { + Map attributes = new HashMap<>(attribs); + attributes.put("newAttribute", new AttributeValue().withS("foo")); + Parameters params = + FakeParameters.getInstance( + BaseClassWithNewAttribute.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + params = + FakeParameters.getInstance( + BaseClassWithUnknownAttributeAnnotation.class, + encryptedAttributes, + null, + TABLE_NAME, + HASH_KEY, + RANGE_KEY); + encryptedAttributes.get("newAttribute").setB(ByteBuffer.allocate(0)); + encryptor.untransform(params); + } + + @Test + public void testEncryptWithFieldLevelDoNotEncryptAnnotation() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotEncryptField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotEncryptField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + } + + @Test + public void testEncryptWithFieldLevelDoNotEncryptAnnotationWithChangedDoNotTouchSuperClass() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotEncryptField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotEncryptField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + + // Change a DoNotTouch value on Mixed super class + encryptedAttributes.put("intValue", new AttributeValue().withN("666")); + Map decryptedAttributes = encryptor.untransform(params); + + Map modifiedAttributes = new HashMap<>(attributes); + modifiedAttributes.put("intValue", new AttributeValue().withN("666")); + + assertThat(decryptedAttributes, AttrMatcher.match(modifiedAttributes)); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testEncryptWithFieldLevelDoNotEncryptAnnotationBadSignature() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotEncryptField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotEncryptField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptedAttributes.put("value", new AttributeValue().withN("200")); + encryptor.untransform(params); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testEncryptWithFieldLevelDoNotEncryptAnnotationBadSignatureSuperClass() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotEncryptField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotEncryptField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + + // Change DoNotEncrypt value on Mixed super class + encryptedAttributes.put("doubleValue", new AttributeValue().withN("200")); + encryptor.untransform(params); + } + + @Test + public void testEncryptWithFieldLevelDoNotTouchAnnotation() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotTouchField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotTouchField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + } + + @Test + public void testEncryptWithFieldLevelDoNotTouchAnnotationChangeValue() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotTouchField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotTouchField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + encryptedAttributes.put("value", new AttributeValue().withN("200")); + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(new AttributeValue().withN("200"), decryptedAttributes.get("value")); + + // Change a DoNotTouch value on Mixed super class + encryptedAttributes.put("intValue", new AttributeValue().withN("666")); + decryptedAttributes = encryptor.untransform(params); + + Map modifiedAttributes = new HashMap<>(attributes); + modifiedAttributes.put("intValue", new AttributeValue().withN("666")); + modifiedAttributes.put("value", new AttributeValue().withN("200")); + + assertThat(decryptedAttributes, AttrMatcher.match(modifiedAttributes)); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testEncryptWithFieldLevelDoNotTouchAnnotationBadSignatureSuperClass() { + Map attributes = new HashMap<>(attribs); + attributes.put("value", new AttributeValue().withN("100")); + Parameters params = + FakeParameters.getInstance( + DoNotTouchField.class, attributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + Map encryptedAttributes = encryptor.transform(params); + assertThat(encryptedAttributes, AttrMatcher.invert(attributes)); + assertAttrEquals(attributes.get("value"), encryptedAttributes.get("value")); + params = + FakeParameters.getInstance( + DoNotTouchField.class, encryptedAttributes, null, TABLE_NAME, HASH_KEY, RANGE_KEY); + + Map decryptedAttributes = encryptor.untransform(params); + assertThat(decryptedAttributes, AttrMatcher.match(attributes)); + + // Change DoNotEncrypt value on Mixed super class + encryptedAttributes.put("doubleValue", new AttributeValue().withN("200")); + encryptor.untransform(params); + } + + private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { + Assert.assertEquals(o1.getB(), o2.getB()); + assertSetsEqual(o1.getBS(), o2.getBS()); + Assert.assertEquals(o1.getN(), o2.getN()); + assertSetsEqual(o1.getNS(), o2.getNS()); + Assert.assertEquals(o1.getS(), o2.getS()); + assertSetsEqual(o1.getSS(), o2.getSS()); + } + + private void assertSetsEqual(Collection c1, Collection c2) { + Assert.assertFalse(c1 == null ^ c2 == null); + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + Assert.assertEquals(s1, s2); + } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java index 521d908a..a60a9f66 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java @@ -2,6 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.fail; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.SaveBehavior; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; @@ -48,12 +54,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.module.SimpleModule; -import org.junit.Before; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.io.File; import java.io.IOException; import java.net.URL; @@ -71,770 +71,831 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; -import static org.testng.AssertJUnit.fail; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.junit.Before; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; public class TransformerHolisticIT { - private static final SecretKey aesKey = new SecretKeySpec(new byte[]{0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, "AES"); - private static final SecretKey hmacKey = new SecretKeySpec(new byte[]{0, - 1, 2, 3, 4, 5, 6, 7}, "HmacSHA256"); - private static final String rsaEncPub = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtiNSLSvT9cExXOcD0dGZ" - + "9DFEMHw8895gAZcCdSppDrxbD7XgZiQYTlgt058i5fS+l11guAUJtKt5sZ2u8Fx0" - + "K9pxMdlczGtvQJdx/LQETEnLnfzAijvHisJ8h6dQOVczM7t01KIkS24QZElyO+kY" - + "qMWLytUV4RSHnrnIuUtPHCe6LieDWT2+1UBguxgtFt1xdXlquACLVv/Em3wp40Xc" - + "bIwzhqLitb98rTY/wqSiGTz1uvvBX46n+f2j3geZKCEDGkWcXYw3dH4lRtDWTbqw" - + "eRcaNDT/MJswQlBk/Up9KCyN7gjX67gttiCO6jMoTNDejGeJhG4Dd2o0vmn8WJlr" - + "5wIDAQAB"; - private static final String rsaEncPriv = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2I1ItK9P1wTFc" - + "5wPR0Zn0MUQwfDzz3mABlwJ1KmkOvFsPteBmJBhOWC3TnyLl9L6XXWC4BQm0q3mx" - + "na7wXHQr2nEx2VzMa29Al3H8tARMScud/MCKO8eKwnyHp1A5VzMzu3TUoiRLbhBk" - + "SXI76RioxYvK1RXhFIeeuci5S08cJ7ouJ4NZPb7VQGC7GC0W3XF1eWq4AItW/8Sb" - + "fCnjRdxsjDOGouK1v3ytNj/CpKIZPPW6+8Ffjqf5/aPeB5koIQMaRZxdjDd0fiVG" - + "0NZNurB5Fxo0NP8wmzBCUGT9Sn0oLI3uCNfruC22II7qMyhM0N6MZ4mEbgN3ajS+" - + "afxYmWvnAgMBAAECggEBAIIU293zDWDZZ73oJ+w0fHXQsdjHAmlRitPX3CN99KZX" - + "k9m2ldudL9bUV3Zqk2wUzgIg6LDEuFfWmAVojsaP4VBopKtriEFfAYfqIbjPgLpT" - + "gh8FoyWW6D6MBJCFyGALjUAHQ7uRScathvt5ESMEqV3wKJTmdsfX97w/B8J+rLN3" - + "3fT3ZJUck5duZ8XKD+UtX1Y3UE1hTWo3Ae2MFND964XyUqy+HaYXjH0x6dhZzqyJ" - + "/OJ/MPGeMJgxp+nUbMWerwxrLQceNFVgnQgHj8e8k4fd04rkowkkPua912gNtmz7" - + "DuIEvcMnY64z585cn+cnXUPJwtu3JbAmn/AyLsV9FLECgYEA798Ut/r+vORB16JD" - + "KFu38pQCgIbdCPkXeI0DC6u1cW8JFhgRqi+AqSrEy5SzY3IY7NVMSRsBI9Y026Bl" - + "R9OQwTrOzLRAw26NPSDvbTkeYXlY9+hX7IovHjGkho/OxyTJ7bKRDYLoNCz56BC1" - + "khIWvECpcf/fZU0nqOFVFqF3H/UCgYEAwmJ4rjl5fksTNtNRL6ivkqkHIPKXzk5w" - + "C+L90HKNicic9bqyX8K4JRkGKSNYN3mkjrguAzUlEld390qNBw5Lu7PwATv0e2i+" - + "6hdwJsjTKNpj7Nh4Mieq6d7lWe1L8FLyHEhxgIeQ4BgqrVtPPOH8IBGpuzVZdWwI" - + "dgOvEvAi/usCgYBdfk3NB/+SEEW5jn0uldE0s4vmHKq6fJwxWIT/X4XxGJ4qBmec" - + "NbeoOAtMbkEdWbNtXBXHyMbA+RTRJctUG5ooNou0Le2wPr6+PMAVilXVGD8dIWpj" - + "v9htpFvENvkZlbU++IKhCY0ICR++3ARpUrOZ3Hou/NRN36y9nlZT48tSoQKBgES2" - + "Bi6fxmBsLUiN/f64xAc1lH2DA0I728N343xRYdK4hTMfYXoUHH+QjurvwXkqmI6S" - + "cEFWAdqv7IoPYjaCSSb6ffYRuWP+LK4WxuAO0QV53SSViDdCalntHmlhRhyXVVnG" - + "CckDIqT0JfHNev7savDzDWpNe2fUXlFJEBPDqrstAoGBAOpd5+QBHF/tP5oPILH4" - + "aD/zmqMH7VtB+b/fOPwtIM+B/WnU7hHLO5t2lJYu18Be3amPkfoQIB7bpkM3Cer2" - + "G7Jw+TcHrY+EtIziDB5vwau1fl4VcbA9SfWpBojJ5Ifo9ELVxGiK95WxeQNSmLUy" - + "7AJzhK1Gwey8a/v+xfqiu9sE"; - private static final PrivateKey rsaPriv; - private static final PublicKey rsaPub; - private static final KeyPair rsaPair; - private static final EncryptionMaterialsProvider symProv; - private static final EncryptionMaterialsProvider asymProv; - private static final EncryptionMaterialsProvider symWrappedProv; - private static final String HASH_KEY = "hashKey"; - private static final String RANGE_KEY = "rangeKey"; - private static final String RSA = "RSA"; - - private AmazonDynamoDB client; - private static AWSKMS kmsClient = AWSKMSClientBuilder.standard().build(); - - private static Map keyDataMap = new HashMap<>(); - - // AttributeEncryptor *must* be used with SaveBehavior.CLOBBER to avoid the risk of data corruption. - private static final DynamoDBMapperConfig CLOBBER_CONFIG = - DynamoDBMapperConfig.builder().withSaveBehavior(SaveBehavior.CLOBBER).build(); - private static final BaseClass ENCRYPTED_TEST_VALUE = new BaseClass(); - private static final Mixed MIXED_TEST_VALUE = new Mixed(); - private static final SignOnly SIGNED_TEST_VALUE = new SignOnly(); - private static final Untouched UNTOUCHED_TEST_VALUE = new Untouched(); - - private static final BaseClass ENCRYPTED_TEST_VALUE_2 = new BaseClass(); - private static final Mixed MIXED_TEST_VALUE_2 = new Mixed(); - private static final SignOnly SIGNED_TEST_VALUE_2 = new SignOnly(); - private static final Untouched UNTOUCHED_TEST_VALUE_2 = new Untouched(); - - private static final String TEST_VECTOR_MANIFEST_DIR = "/vectors/encrypted_item/"; - private static final String SCENARIO_MANIFEST_PATH = TEST_VECTOR_MANIFEST_DIR + "scenarios.json"; - private static final String JAVA_DIR = "java"; - - static { - try { - KeyFactory rsaFact = KeyFactory.getInstance("RSA"); - rsaPub = rsaFact.generatePublic(new X509EncodedKeySpec(Base64 - .decode(rsaEncPub))); - rsaPriv = rsaFact.generatePrivate(new PKCS8EncodedKeySpec(Base64 - .decode(rsaEncPriv))); - rsaPair = new KeyPair(rsaPub, rsaPriv); - } catch (GeneralSecurityException ex) { - throw new RuntimeException(ex); - } - symProv = new SymmetricStaticProvider(aesKey, hmacKey); - asymProv = new AsymmetricStaticProvider(rsaPair, rsaPair); - symWrappedProv = new WrappedMaterialsProvider(aesKey, aesKey, hmacKey); - - ENCRYPTED_TEST_VALUE.setHashKey(5); - ENCRYPTED_TEST_VALUE.setRangeKey(7); - ENCRYPTED_TEST_VALUE.setVersion(0); - ENCRYPTED_TEST_VALUE.setIntValue(123); - ENCRYPTED_TEST_VALUE.setStringValue("Hello world!"); - ENCRYPTED_TEST_VALUE.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - ENCRYPTED_TEST_VALUE.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - ENCRYPTED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, - 200, 10, 15, 0))); - - MIXED_TEST_VALUE.setHashKey(6); - MIXED_TEST_VALUE.setRangeKey(8); - MIXED_TEST_VALUE.setVersion(0); - MIXED_TEST_VALUE.setIntValue(123); - MIXED_TEST_VALUE.setStringValue("Hello world!"); - MIXED_TEST_VALUE.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - MIXED_TEST_VALUE.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - MIXED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, 200, - 10, 15, 0))); - - SIGNED_TEST_VALUE.setHashKey(8); - SIGNED_TEST_VALUE.setRangeKey(10); - SIGNED_TEST_VALUE.setVersion(0); - SIGNED_TEST_VALUE.setIntValue(123); - SIGNED_TEST_VALUE.setStringValue("Hello world!"); - SIGNED_TEST_VALUE.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - SIGNED_TEST_VALUE.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - SIGNED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, 200, - 10, 15, 0))); - - UNTOUCHED_TEST_VALUE.setHashKey(7); - UNTOUCHED_TEST_VALUE.setRangeKey(9); - UNTOUCHED_TEST_VALUE.setVersion(0); - UNTOUCHED_TEST_VALUE.setIntValue(123); - UNTOUCHED_TEST_VALUE.setStringValue("Hello world!"); - UNTOUCHED_TEST_VALUE.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - UNTOUCHED_TEST_VALUE.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - UNTOUCHED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, - 200, 10, 15, 0))); - - // Now storing doubles - ENCRYPTED_TEST_VALUE_2.setHashKey(5); - ENCRYPTED_TEST_VALUE_2.setRangeKey(7); - ENCRYPTED_TEST_VALUE_2.setVersion(0); - ENCRYPTED_TEST_VALUE_2.setIntValue(123); - ENCRYPTED_TEST_VALUE_2.setStringValue("Hello world!"); - ENCRYPTED_TEST_VALUE_2.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - ENCRYPTED_TEST_VALUE_2.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - ENCRYPTED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, - 200, 10, 15, 0))); - ENCRYPTED_TEST_VALUE_2.setDoubleValue(15); - ENCRYPTED_TEST_VALUE_2.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - MIXED_TEST_VALUE_2.setHashKey(6); - MIXED_TEST_VALUE_2.setRangeKey(8); - MIXED_TEST_VALUE_2.setVersion(0); - MIXED_TEST_VALUE_2.setIntValue(123); - MIXED_TEST_VALUE_2.setStringValue("Hello world!"); - MIXED_TEST_VALUE_2.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - MIXED_TEST_VALUE_2.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - MIXED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, 200, - 10, 15, 0))); - MIXED_TEST_VALUE_2.setDoubleValue(15); - MIXED_TEST_VALUE_2.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - SIGNED_TEST_VALUE_2.setHashKey(8); - SIGNED_TEST_VALUE_2.setRangeKey(10); - SIGNED_TEST_VALUE_2.setVersion(0); - SIGNED_TEST_VALUE_2.setIntValue(123); - SIGNED_TEST_VALUE_2.setStringValue("Hello world!"); - SIGNED_TEST_VALUE_2.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - SIGNED_TEST_VALUE_2.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - SIGNED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, 200, - 10, 15, 0))); - SIGNED_TEST_VALUE_2.setDoubleValue(15); - SIGNED_TEST_VALUE_2.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - UNTOUCHED_TEST_VALUE_2.setHashKey(7); - UNTOUCHED_TEST_VALUE_2.setRangeKey(9); - UNTOUCHED_TEST_VALUE_2.setVersion(0); - UNTOUCHED_TEST_VALUE_2.setIntValue(123); - UNTOUCHED_TEST_VALUE_2.setStringValue("Hello world!"); - UNTOUCHED_TEST_VALUE_2.setByteArrayValue(new byte[]{0, 1, 2, 3, 4, 5}); - UNTOUCHED_TEST_VALUE_2.setStringSet(new HashSet(Arrays.asList( - "Goodbye", "Cruel", "World", "?"))); - UNTOUCHED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, - 200, 10, 15, 0))); - UNTOUCHED_TEST_VALUE_2.setDoubleValue(15); - UNTOUCHED_TEST_VALUE_2.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - } - - @DataProvider(name = "getEncryptTestVectors") - public static Object[][] getEncryptTestVectors() throws IOException { - ScenarioManifest scenarioManifest = getManifestFromFile(SCENARIO_MANIFEST_PATH, - new TypeReference() {}); - loadKeyData(scenarioManifest.keyDataPath); - - // Only use Java generated test vectors to dedupe the scenarios for encrypt, - // we only care that we are able to generate data using the different provider configurations - List dedupedScenarios = scenarioManifest.scenarios.stream() - .filter(s -> s.ciphertextPath.contains(JAVA_DIR)) - .map(s -> new Object[] { s }) - .collect(Collectors.toList()); - return dedupedScenarios.toArray(new Object[dedupedScenarios.size()][]); - } - - @DataProvider(name = "getDecryptTestVectors") - public static Object[][] getDecryptTestVectors() throws IOException { - ScenarioManifest scenarioManifest = getManifestFromFile(SCENARIO_MANIFEST_PATH, - new TypeReference() {}); - loadKeyData(scenarioManifest.keyDataPath); - - List scenarios = scenarioManifest.scenarios.stream() - .map(s -> new Object[] { s }) - .collect(Collectors.toList()); - return scenarios.toArray(new Object[scenarios.size()][]); + private static final SecretKey aesKey = + new SecretKeySpec(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, "AES"); + private static final SecretKey hmacKey = + new SecretKeySpec(new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, "HmacSHA256"); + private static final String rsaEncPub = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtiNSLSvT9cExXOcD0dGZ" + + "9DFEMHw8895gAZcCdSppDrxbD7XgZiQYTlgt058i5fS+l11guAUJtKt5sZ2u8Fx0" + + "K9pxMdlczGtvQJdx/LQETEnLnfzAijvHisJ8h6dQOVczM7t01KIkS24QZElyO+kY" + + "qMWLytUV4RSHnrnIuUtPHCe6LieDWT2+1UBguxgtFt1xdXlquACLVv/Em3wp40Xc" + + "bIwzhqLitb98rTY/wqSiGTz1uvvBX46n+f2j3geZKCEDGkWcXYw3dH4lRtDWTbqw" + + "eRcaNDT/MJswQlBk/Up9KCyN7gjX67gttiCO6jMoTNDejGeJhG4Dd2o0vmn8WJlr" + + "5wIDAQAB"; + private static final String rsaEncPriv = + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2I1ItK9P1wTFc" + + "5wPR0Zn0MUQwfDzz3mABlwJ1KmkOvFsPteBmJBhOWC3TnyLl9L6XXWC4BQm0q3mx" + + "na7wXHQr2nEx2VzMa29Al3H8tARMScud/MCKO8eKwnyHp1A5VzMzu3TUoiRLbhBk" + + "SXI76RioxYvK1RXhFIeeuci5S08cJ7ouJ4NZPb7VQGC7GC0W3XF1eWq4AItW/8Sb" + + "fCnjRdxsjDOGouK1v3ytNj/CpKIZPPW6+8Ffjqf5/aPeB5koIQMaRZxdjDd0fiVG" + + "0NZNurB5Fxo0NP8wmzBCUGT9Sn0oLI3uCNfruC22II7qMyhM0N6MZ4mEbgN3ajS+" + + "afxYmWvnAgMBAAECggEBAIIU293zDWDZZ73oJ+w0fHXQsdjHAmlRitPX3CN99KZX" + + "k9m2ldudL9bUV3Zqk2wUzgIg6LDEuFfWmAVojsaP4VBopKtriEFfAYfqIbjPgLpT" + + "gh8FoyWW6D6MBJCFyGALjUAHQ7uRScathvt5ESMEqV3wKJTmdsfX97w/B8J+rLN3" + + "3fT3ZJUck5duZ8XKD+UtX1Y3UE1hTWo3Ae2MFND964XyUqy+HaYXjH0x6dhZzqyJ" + + "/OJ/MPGeMJgxp+nUbMWerwxrLQceNFVgnQgHj8e8k4fd04rkowkkPua912gNtmz7" + + "DuIEvcMnY64z585cn+cnXUPJwtu3JbAmn/AyLsV9FLECgYEA798Ut/r+vORB16JD" + + "KFu38pQCgIbdCPkXeI0DC6u1cW8JFhgRqi+AqSrEy5SzY3IY7NVMSRsBI9Y026Bl" + + "R9OQwTrOzLRAw26NPSDvbTkeYXlY9+hX7IovHjGkho/OxyTJ7bKRDYLoNCz56BC1" + + "khIWvECpcf/fZU0nqOFVFqF3H/UCgYEAwmJ4rjl5fksTNtNRL6ivkqkHIPKXzk5w" + + "C+L90HKNicic9bqyX8K4JRkGKSNYN3mkjrguAzUlEld390qNBw5Lu7PwATv0e2i+" + + "6hdwJsjTKNpj7Nh4Mieq6d7lWe1L8FLyHEhxgIeQ4BgqrVtPPOH8IBGpuzVZdWwI" + + "dgOvEvAi/usCgYBdfk3NB/+SEEW5jn0uldE0s4vmHKq6fJwxWIT/X4XxGJ4qBmec" + + "NbeoOAtMbkEdWbNtXBXHyMbA+RTRJctUG5ooNou0Le2wPr6+PMAVilXVGD8dIWpj" + + "v9htpFvENvkZlbU++IKhCY0ICR++3ARpUrOZ3Hou/NRN36y9nlZT48tSoQKBgES2" + + "Bi6fxmBsLUiN/f64xAc1lH2DA0I728N343xRYdK4hTMfYXoUHH+QjurvwXkqmI6S" + + "cEFWAdqv7IoPYjaCSSb6ffYRuWP+LK4WxuAO0QV53SSViDdCalntHmlhRhyXVVnG" + + "CckDIqT0JfHNev7savDzDWpNe2fUXlFJEBPDqrstAoGBAOpd5+QBHF/tP5oPILH4" + + "aD/zmqMH7VtB+b/fOPwtIM+B/WnU7hHLO5t2lJYu18Be3amPkfoQIB7bpkM3Cer2" + + "G7Jw+TcHrY+EtIziDB5vwau1fl4VcbA9SfWpBojJ5Ifo9ELVxGiK95WxeQNSmLUy" + + "7AJzhK1Gwey8a/v+xfqiu9sE"; + private static final PrivateKey rsaPriv; + private static final PublicKey rsaPub; + private static final KeyPair rsaPair; + private static final EncryptionMaterialsProvider symProv; + private static final EncryptionMaterialsProvider asymProv; + private static final EncryptionMaterialsProvider symWrappedProv; + private static final String HASH_KEY = "hashKey"; + private static final String RANGE_KEY = "rangeKey"; + private static final String RSA = "RSA"; + + private AmazonDynamoDB client; + private static AWSKMS kmsClient = AWSKMSClientBuilder.standard().build(); + + private static Map keyDataMap = new HashMap<>(); + + // AttributeEncryptor *must* be used with SaveBehavior.CLOBBER to avoid the risk of data + // corruption. + private static final DynamoDBMapperConfig CLOBBER_CONFIG = + DynamoDBMapperConfig.builder().withSaveBehavior(SaveBehavior.CLOBBER).build(); + private static final BaseClass ENCRYPTED_TEST_VALUE = new BaseClass(); + private static final Mixed MIXED_TEST_VALUE = new Mixed(); + private static final SignOnly SIGNED_TEST_VALUE = new SignOnly(); + private static final Untouched UNTOUCHED_TEST_VALUE = new Untouched(); + + private static final BaseClass ENCRYPTED_TEST_VALUE_2 = new BaseClass(); + private static final Mixed MIXED_TEST_VALUE_2 = new Mixed(); + private static final SignOnly SIGNED_TEST_VALUE_2 = new SignOnly(); + private static final Untouched UNTOUCHED_TEST_VALUE_2 = new Untouched(); + + private static final String TEST_VECTOR_MANIFEST_DIR = "/vectors/encrypted_item/"; + private static final String SCENARIO_MANIFEST_PATH = TEST_VECTOR_MANIFEST_DIR + "scenarios.json"; + private static final String JAVA_DIR = "java"; + + static { + try { + KeyFactory rsaFact = KeyFactory.getInstance("RSA"); + rsaPub = rsaFact.generatePublic(new X509EncodedKeySpec(Base64.decode(rsaEncPub))); + rsaPriv = rsaFact.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(rsaEncPriv))); + rsaPair = new KeyPair(rsaPub, rsaPriv); + } catch (GeneralSecurityException ex) { + throw new RuntimeException(ex); } - - // Set up for non-parameterized tests - @Before - public void setUp() { - System.setProperty("sqlite4java.library.path", "target/test-lib"); - client = DynamoDBEmbedded.create(); - - // load data into ciphertext tables - createCiphertextTables(client); - } - - @Test(dataProvider = "getDecryptTestVectors") - public void decryptTestVector(Scenario scenario) throws IOException { - System.setProperty("sqlite4java.library.path", "target/test-lib"); - client = DynamoDBEmbedded.create(); - - // load data into ciphertext tables - createCiphertextTables(client); - - // load data from vector file - putDataFromFile(client, scenario.ciphertextPath); - - // create and load metastore table if necessary - ProviderStore metastore = null; - if (scenario.metastore != null) { - MetaStore.createTable(client, scenario.metastore.tableName, new ProvisionedThroughput(100L, 100L)); - putDataFromFile(client, scenario.metastore.path); - EncryptionMaterialsProvider metaProvider = createProvider(scenario.metastore.providerName, - scenario.materialName, scenario.metastore.keys, null); - metastore = new MetaStore(client, scenario.metastore.tableName, DynamoDBEncryptor.getInstance(metaProvider)); - } - - // Create the mapper with the provider under test - EncryptionMaterialsProvider provider = createProvider(scenario.providerName, scenario.materialName, scenario.keys, metastore); - DynamoDBMapper mapper = new DynamoDBMapper(client, - new DynamoDBMapperConfig(SaveBehavior.CLOBBER), new AttributeEncryptor(provider)); - - // Verify successful decryption - switch (scenario.version) { - case "v0": - assertVersionCompatibility(mapper); - break; - case "v1": - assertVersionCompatibility_2(mapper); - break; - default: - throw new IllegalStateException("Version " + scenario.version + " not yet implemented in test vector runner"); - } + symProv = new SymmetricStaticProvider(aesKey, hmacKey); + asymProv = new AsymmetricStaticProvider(rsaPair, rsaPair); + symWrappedProv = new WrappedMaterialsProvider(aesKey, aesKey, hmacKey); + + ENCRYPTED_TEST_VALUE.setHashKey(5); + ENCRYPTED_TEST_VALUE.setRangeKey(7); + ENCRYPTED_TEST_VALUE.setVersion(0); + ENCRYPTED_TEST_VALUE.setIntValue(123); + ENCRYPTED_TEST_VALUE.setStringValue("Hello world!"); + ENCRYPTED_TEST_VALUE.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + ENCRYPTED_TEST_VALUE.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + ENCRYPTED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + + MIXED_TEST_VALUE.setHashKey(6); + MIXED_TEST_VALUE.setRangeKey(8); + MIXED_TEST_VALUE.setVersion(0); + MIXED_TEST_VALUE.setIntValue(123); + MIXED_TEST_VALUE.setStringValue("Hello world!"); + MIXED_TEST_VALUE.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + MIXED_TEST_VALUE.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + MIXED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + + SIGNED_TEST_VALUE.setHashKey(8); + SIGNED_TEST_VALUE.setRangeKey(10); + SIGNED_TEST_VALUE.setVersion(0); + SIGNED_TEST_VALUE.setIntValue(123); + SIGNED_TEST_VALUE.setStringValue("Hello world!"); + SIGNED_TEST_VALUE.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + SIGNED_TEST_VALUE.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + SIGNED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + + UNTOUCHED_TEST_VALUE.setHashKey(7); + UNTOUCHED_TEST_VALUE.setRangeKey(9); + UNTOUCHED_TEST_VALUE.setVersion(0); + UNTOUCHED_TEST_VALUE.setIntValue(123); + UNTOUCHED_TEST_VALUE.setStringValue("Hello world!"); + UNTOUCHED_TEST_VALUE.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + UNTOUCHED_TEST_VALUE.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + UNTOUCHED_TEST_VALUE.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + + // Now storing doubles + ENCRYPTED_TEST_VALUE_2.setHashKey(5); + ENCRYPTED_TEST_VALUE_2.setRangeKey(7); + ENCRYPTED_TEST_VALUE_2.setVersion(0); + ENCRYPTED_TEST_VALUE_2.setIntValue(123); + ENCRYPTED_TEST_VALUE_2.setStringValue("Hello world!"); + ENCRYPTED_TEST_VALUE_2.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + ENCRYPTED_TEST_VALUE_2.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + ENCRYPTED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + ENCRYPTED_TEST_VALUE_2.setDoubleValue(15); + ENCRYPTED_TEST_VALUE_2.setDoubleSet( + new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + + MIXED_TEST_VALUE_2.setHashKey(6); + MIXED_TEST_VALUE_2.setRangeKey(8); + MIXED_TEST_VALUE_2.setVersion(0); + MIXED_TEST_VALUE_2.setIntValue(123); + MIXED_TEST_VALUE_2.setStringValue("Hello world!"); + MIXED_TEST_VALUE_2.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + MIXED_TEST_VALUE_2.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + MIXED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + MIXED_TEST_VALUE_2.setDoubleValue(15); + MIXED_TEST_VALUE_2.setDoubleSet( + new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + + SIGNED_TEST_VALUE_2.setHashKey(8); + SIGNED_TEST_VALUE_2.setRangeKey(10); + SIGNED_TEST_VALUE_2.setVersion(0); + SIGNED_TEST_VALUE_2.setIntValue(123); + SIGNED_TEST_VALUE_2.setStringValue("Hello world!"); + SIGNED_TEST_VALUE_2.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + SIGNED_TEST_VALUE_2.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + SIGNED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + SIGNED_TEST_VALUE_2.setDoubleValue(15); + SIGNED_TEST_VALUE_2.setDoubleSet( + new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + + UNTOUCHED_TEST_VALUE_2.setHashKey(7); + UNTOUCHED_TEST_VALUE_2.setRangeKey(9); + UNTOUCHED_TEST_VALUE_2.setVersion(0); + UNTOUCHED_TEST_VALUE_2.setIntValue(123); + UNTOUCHED_TEST_VALUE_2.setStringValue("Hello world!"); + UNTOUCHED_TEST_VALUE_2.setByteArrayValue(new byte[] {0, 1, 2, 3, 4, 5}); + UNTOUCHED_TEST_VALUE_2.setStringSet( + new HashSet(Arrays.asList("Goodbye", "Cruel", "World", "?"))); + UNTOUCHED_TEST_VALUE_2.setIntSet(new HashSet(Arrays.asList(1, 200, 10, 15, 0))); + UNTOUCHED_TEST_VALUE_2.setDoubleValue(15); + UNTOUCHED_TEST_VALUE_2.setDoubleSet( + new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + } + + @DataProvider(name = "getEncryptTestVectors") + public static Object[][] getEncryptTestVectors() throws IOException { + ScenarioManifest scenarioManifest = + getManifestFromFile(SCENARIO_MANIFEST_PATH, new TypeReference() {}); + loadKeyData(scenarioManifest.keyDataPath); + + // Only use Java generated test vectors to dedupe the scenarios for encrypt, + // we only care that we are able to generate data using the different provider configurations + List dedupedScenarios = + scenarioManifest.scenarios.stream() + .filter(s -> s.ciphertextPath.contains(JAVA_DIR)) + .map(s -> new Object[] {s}) + .collect(Collectors.toList()); + return dedupedScenarios.toArray(new Object[dedupedScenarios.size()][]); + } + + @DataProvider(name = "getDecryptTestVectors") + public static Object[][] getDecryptTestVectors() throws IOException { + ScenarioManifest scenarioManifest = + getManifestFromFile(SCENARIO_MANIFEST_PATH, new TypeReference() {}); + loadKeyData(scenarioManifest.keyDataPath); + + List scenarios = + scenarioManifest.scenarios.stream().map(s -> new Object[] {s}).collect(Collectors.toList()); + return scenarios.toArray(new Object[scenarios.size()][]); + } + + // Set up for non-parameterized tests + @Before + public void setUp() { + System.setProperty("sqlite4java.library.path", "target/test-lib"); + client = DynamoDBEmbedded.create(); + + // load data into ciphertext tables + createCiphertextTables(client); + } + + @Test(dataProvider = "getDecryptTestVectors") + public void decryptTestVector(Scenario scenario) throws IOException { + System.setProperty("sqlite4java.library.path", "target/test-lib"); + client = DynamoDBEmbedded.create(); + + // load data into ciphertext tables + createCiphertextTables(client); + + // load data from vector file + putDataFromFile(client, scenario.ciphertextPath); + + // create and load metastore table if necessary + ProviderStore metastore = null; + if (scenario.metastore != null) { + MetaStore.createTable( + client, scenario.metastore.tableName, new ProvisionedThroughput(100L, 100L)); + putDataFromFile(client, scenario.metastore.path); + EncryptionMaterialsProvider metaProvider = + createProvider( + scenario.metastore.providerName, + scenario.materialName, + scenario.metastore.keys, + null); + metastore = + new MetaStore( + client, scenario.metastore.tableName, DynamoDBEncryptor.getInstance(metaProvider)); } - @Test(dataProvider = "getEncryptTestVectors") - public void encryptWithTestVector(Scenario scenario) throws IOException { - System.setProperty("sqlite4java.library.path", "target/test-lib"); - client = DynamoDBEmbedded.create(); - - // load data into ciphertext tables - createCiphertextTables(client); - - // create and load metastore table if necessary - ProviderStore metastore = null; - if (scenario.metastore != null) { - MetaStore.createTable(client, scenario.metastore.tableName, new ProvisionedThroughput(100L, 100L)); - putDataFromFile(client, scenario.metastore.path); - EncryptionMaterialsProvider metaProvider = createProvider(scenario.metastore.providerName, - scenario.materialName, scenario.metastore.keys, null); - metastore = new MetaStore(client, scenario.metastore.tableName, DynamoDBEncryptor.getInstance(metaProvider)); - } - - // Encrypt data with the provider under test, only ensure that no exception is thrown - EncryptionMaterialsProvider provider = createProvider(scenario.providerName, scenario.materialName, scenario.keys, metastore); - generateStandardData(provider); + // Create the mapper with the provider under test + EncryptionMaterialsProvider provider = + createProvider(scenario.providerName, scenario.materialName, scenario.keys, metastore); + DynamoDBMapper mapper = + new DynamoDBMapper( + client, + new DynamoDBMapperConfig(SaveBehavior.CLOBBER), + new AttributeEncryptor(provider)); + + // Verify successful decryption + switch (scenario.version) { + case "v0": + assertVersionCompatibility(mapper); + break; + case "v1": + assertVersionCompatibility_2(mapper); + break; + default: + throw new IllegalStateException( + "Version " + scenario.version + " not yet implemented in test vector runner"); } - - @Test - public void simpleSaveLoad() { - DynamoDBMapper mapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); - Mixed obj = new Mixed(); - obj.setHashKey(0); - obj.setRangeKey(15); - obj.setIntSet(new HashSet()); - obj.getIntSet().add(3); - obj.getIntSet().add(5); - obj.getIntSet().add(7); - obj.setDoubleValue(15); - obj.setStringValue("Blargh!"); - obj.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - mapper.save(obj); - - Mixed result = mapper.load(Mixed.class, 0, 15); - assertEquals(obj, result); - - result.setStringValue("Foo"); - mapper.save(result); - - Mixed result2 = mapper.load(Mixed.class, 0, 15); - assertEquals(result, result2); - - mapper.delete(result); - assertNull(mapper.load(Mixed.class, 0, 15)); + } + + @Test(dataProvider = "getEncryptTestVectors") + public void encryptWithTestVector(Scenario scenario) throws IOException { + System.setProperty("sqlite4java.library.path", "target/test-lib"); + client = DynamoDBEmbedded.create(); + + // load data into ciphertext tables + createCiphertextTables(client); + + // create and load metastore table if necessary + ProviderStore metastore = null; + if (scenario.metastore != null) { + MetaStore.createTable( + client, scenario.metastore.tableName, new ProvisionedThroughput(100L, 100L)); + putDataFromFile(client, scenario.metastore.path); + EncryptionMaterialsProvider metaProvider = + createProvider( + scenario.metastore.providerName, + scenario.materialName, + scenario.metastore.keys, + null); + metastore = + new MetaStore( + client, scenario.metastore.tableName, DynamoDBEncryptor.getInstance(metaProvider)); } - /** - * This test ensures that optimistic locking can be successfully done through the {@link DynamoDBMapper} when - * combined with the {@link AttributeEncryptor}. Specifically it checks that {@link SaveBehavior#PUT} properly - * enforces versioning and will result in a {@link ConditionalCheckFailedException} when optimistic locking should - * prevent a write. Finally, it checks that {@link SaveBehavior#CLOBBER} properly ignores optimistic locking and - * overwrites the old value. + // Encrypt data with the provider under test, only ensure that no exception is thrown + EncryptionMaterialsProvider provider = + createProvider(scenario.providerName, scenario.materialName, scenario.keys, metastore); + generateStandardData(provider); + } + + @Test + public void simpleSaveLoad() { + DynamoDBMapper mapper = + new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); + Mixed obj = new Mixed(); + obj.setHashKey(0); + obj.setRangeKey(15); + obj.setIntSet(new HashSet()); + obj.getIntSet().add(3); + obj.getIntSet().add(5); + obj.getIntSet().add(7); + obj.setDoubleValue(15); + obj.setStringValue("Blargh!"); + obj.setDoubleSet(new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + + mapper.save(obj); + + Mixed result = mapper.load(Mixed.class, 0, 15); + assertEquals(obj, result); + + result.setStringValue("Foo"); + mapper.save(result); + + Mixed result2 = mapper.load(Mixed.class, 0, 15); + assertEquals(result, result2); + + mapper.delete(result); + assertNull(mapper.load(Mixed.class, 0, 15)); + } + + /** + * This test ensures that optimistic locking can be successfully done through the {@link + * DynamoDBMapper} when combined with the {@link AttributeEncryptor}. Specifically it checks that + * {@link SaveBehavior#PUT} properly enforces versioning and will result in a {@link + * ConditionalCheckFailedException} when optimistic locking should prevent a write. Finally, it + * checks that {@link SaveBehavior#CLOBBER} properly ignores optimistic locking and overwrites the + * old value. + */ + @Test + public void optimisticLockingTest() { + DynamoDBMapper mapper = + new DynamoDBMapper( + client, + DynamoDBMapperConfig.builder().withSaveBehavior(SaveBehavior.PUT).build(), + new AttributeEncryptor(symProv)); + DynamoDBMapper clobberMapper = + new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); + + /* + * Lineage of objects + * expected -> v1 -> v2 -> v3 + * | + * -> v2_2 -> clobbered + * Splitting the lineage after v1 is what should + * cause the ConditionalCheckFailedException. */ - @Test - public void optimisticLockingTest() { - DynamoDBMapper mapper = new DynamoDBMapper(client, - DynamoDBMapperConfig.builder() - .withSaveBehavior(SaveBehavior.PUT).build(), - new AttributeEncryptor(symProv)); - DynamoDBMapper clobberMapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); - - /* - * Lineage of objects - * expected -> v1 -> v2 -> v3 - * | - * -> v2_2 -> clobbered - * Splitting the lineage after v1 is what should - * cause the ConditionalCheckFailedException. - */ - final int hashKey = 0; - final int rangeKey = 15; - final Mixed expected = new Mixed(); - expected.setHashKey(hashKey); - expected.setRangeKey(rangeKey); - expected.setIntSet(new HashSet()); - expected.getIntSet().add(3); - expected.getIntSet().add(5); - expected.getIntSet().add(7); - expected.setDoubleValue(15); - expected.setStringValue("Blargh!"); - expected.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - mapper.save(expected); - Mixed v1 = mapper.load(Mixed.class, hashKey, rangeKey); - assertEquals(expected, v1); - v1.setStringValue("New value"); - mapper.save(v1); - Mixed v2 = mapper.load(Mixed.class, hashKey, rangeKey); - assertEquals(v1, v2); - Mixed v2_2 = mapper.load(Mixed.class, hashKey, rangeKey); - - v2.getIntSet().add(-37); - mapper.save(v2); - Mixed v3 = mapper.load(Mixed.class, hashKey, rangeKey); - assertEquals(v2, v3); - assertTrue(v3.getIntSet().contains(-37)); - - // This should fail due to optimistic locking - v2_2.getIntSet().add(38); - try { - mapper.save(v2_2); - fail("Expected ConditionalCheckFailedException"); - } catch (ConditionalCheckFailedException ex) { - // Expected exception - } - - // Force the update with clobber - clobberMapper.save(v2_2); - Mixed clobbered = mapper.load(Mixed.class, hashKey, rangeKey); - assertEquals(v2_2, clobbered); - assertTrue(clobbered.getIntSet().contains(38)); - assertFalse(clobbered.getIntSet().contains(-37)); + final int hashKey = 0; + final int rangeKey = 15; + final Mixed expected = new Mixed(); + expected.setHashKey(hashKey); + expected.setRangeKey(rangeKey); + expected.setIntSet(new HashSet()); + expected.getIntSet().add(3); + expected.getIntSet().add(5); + expected.getIntSet().add(7); + expected.setDoubleValue(15); + expected.setStringValue("Blargh!"); + expected.setDoubleSet(new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + + mapper.save(expected); + Mixed v1 = mapper.load(Mixed.class, hashKey, rangeKey); + assertEquals(expected, v1); + v1.setStringValue("New value"); + mapper.save(v1); + Mixed v2 = mapper.load(Mixed.class, hashKey, rangeKey); + assertEquals(v1, v2); + Mixed v2_2 = mapper.load(Mixed.class, hashKey, rangeKey); + + v2.getIntSet().add(-37); + mapper.save(v2); + Mixed v3 = mapper.load(Mixed.class, hashKey, rangeKey); + assertEquals(v2, v3); + assertTrue(v3.getIntSet().contains(-37)); + + // This should fail due to optimistic locking + v2_2.getIntSet().add(38); + try { + mapper.save(v2_2); + fail("Expected ConditionalCheckFailedException"); + } catch (ConditionalCheckFailedException ex) { + // Expected exception } - @Test - public void leadingAndTrailingZeros() { - DynamoDBMapper mapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); - Mixed obj = new Mixed(); - obj.setHashKey(0); - obj.setRangeKey(15); - obj.setIntSet(new HashSet()); - obj.getIntSet().add(3); - obj.getIntSet().add(5); - obj.getIntSet().add(7); - obj.setStringValue("Blargh!"); - obj.setDoubleValue(15); - obj.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - - mapper.save(obj); - - - // TODO: Update the mock to handle this appropriately. - // DynamoDb discards leading and trailing zeros from numbers - Map key = new HashMap(); - key.put(HASH_KEY, new AttributeValue().withN("0")); - key.put(RANGE_KEY, new AttributeValue().withN("15")); - Map attributeUpdates = new HashMap(); - attributeUpdates.put("doubleValue", new AttributeValueUpdate(new AttributeValue().withN("15"), AttributeAction.PUT)); - UpdateItemRequest update = new UpdateItemRequest("TableName", key, attributeUpdates); - client.updateItem(update); - - - Mixed result = mapper.load(Mixed.class, 0, 15); - assertEquals(obj, result); - - result.setStringValue("Foo"); - mapper.save(result); - - Mixed result2 = mapper.load(Mixed.class, 0, 15); - assertEquals(result, result2); - - mapper.delete(result); - assertNull(mapper.load(Mixed.class, 0, 15)); + // Force the update with clobber + clobberMapper.save(v2_2); + Mixed clobbered = mapper.load(Mixed.class, hashKey, rangeKey); + assertEquals(v2_2, clobbered); + assertTrue(clobbered.getIntSet().contains(38)); + assertFalse(clobbered.getIntSet().contains(-37)); + } + + @Test + public void leadingAndTrailingZeros() { + DynamoDBMapper mapper = + new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); + Mixed obj = new Mixed(); + obj.setHashKey(0); + obj.setRangeKey(15); + obj.setIntSet(new HashSet()); + obj.getIntSet().add(3); + obj.getIntSet().add(5); + obj.getIntSet().add(7); + obj.setStringValue("Blargh!"); + obj.setDoubleValue(15); + obj.setDoubleSet(new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + + mapper.save(obj); + + // TODO: Update the mock to handle this appropriately. + // DynamoDb discards leading and trailing zeros from numbers + Map key = new HashMap(); + key.put(HASH_KEY, new AttributeValue().withN("0")); + key.put(RANGE_KEY, new AttributeValue().withN("15")); + Map attributeUpdates = + new HashMap(); + attributeUpdates.put( + "doubleValue", + new AttributeValueUpdate(new AttributeValue().withN("15"), AttributeAction.PUT)); + UpdateItemRequest update = new UpdateItemRequest("TableName", key, attributeUpdates); + client.updateItem(update); + + Mixed result = mapper.load(Mixed.class, 0, 15); + assertEquals(obj, result); + + result.setStringValue("Foo"); + mapper.save(result); + + Mixed result2 = mapper.load(Mixed.class, 0, 15); + assertEquals(result, result2); + + mapper.delete(result); + assertNull(mapper.load(Mixed.class, 0, 15)); + } + + @Test + public void simpleSaveLoadAsym() { + DynamoDBMapper mapper = + new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(asymProv)); + + BaseClass obj = new BaseClass(); + obj.setHashKey(0); + obj.setRangeKey(15); + obj.setIntSet(new HashSet()); + obj.getIntSet().add(3); + obj.getIntSet().add(5); + obj.getIntSet().add(7); + obj.setDoubleValue(15); + obj.setStringValue("Blargh!"); + obj.setDoubleSet(new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); + mapper.save(obj); + + BaseClass result = mapper.load(BaseClass.class, 0, 15); + assertEquals(obj, result); + + result.setStringValue("Foo"); + mapper.save(result); + + BaseClass result2 = mapper.load(BaseClass.class, 0, 15); + assertEquals(result, result2); + + mapper.delete(result); + assertNull(mapper.load(BaseClass.class, 0, 15)); + } + + @Test + public void simpleSaveLoadHashOnly() { + DynamoDBMapper mapper = + new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv)); + + HashKeyOnly obj = new HashKeyOnly(""); + obj.setHashKey("Foo"); + + mapper.save(obj); + + HashKeyOnly result = mapper.load(HashKeyOnly.class, "Foo"); + assertEquals(obj, result); + + mapper.delete(obj); + assertNull(mapper.load(BaseClass.class, 0, 15)); + } + + @Test + public void simpleSaveLoadKeysOnly() { + DynamoDBMapper mapper = + new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(asymProv)); + + KeysOnly obj = new KeysOnly(); + obj.setHashKey(0); + obj.setRangeKey(15); + + mapper.save(obj); + + KeysOnly result = mapper.load(KeysOnly.class, 0, 15); + assertEquals(obj, result); + + mapper.delete(obj); + assertNull(mapper.load(BaseClass.class, 0, 15)); + } + + public void generateStandardData(EncryptionMaterialsProvider prov) { + DynamoDBMapper mapper = + new DynamoDBMapper( + client, new DynamoDBMapperConfig(SaveBehavior.CLOBBER), new AttributeEncryptor(prov)); + mapper.save(new HashKeyOnly("Foo")); + mapper.save(new HashKeyOnly("Bar")); + mapper.save(new HashKeyOnly("Baz")); + + mapper.save(new KeysOnly(0, 1)); + mapper.save(new KeysOnly(0, 2)); + mapper.save(new KeysOnly(0, 3)); + mapper.save(new KeysOnly(1, 1)); + mapper.save(new KeysOnly(1, 2)); + mapper.save(new KeysOnly(1, 3)); + mapper.save(new KeysOnly(5, 1)); + mapper.save(new KeysOnly(6, 2)); + mapper.save(new KeysOnly(7, 3)); + + mapper.save(ENCRYPTED_TEST_VALUE_2); + mapper.save(MIXED_TEST_VALUE_2); + mapper.save(SIGNED_TEST_VALUE_2); + mapper.save(UNTOUCHED_TEST_VALUE_2); + + // Uncomment the function below to print the generated data + // in our test vector format. + + // printTablesAsTestVectors(); + } + + private void assertVersionCompatibility(DynamoDBMapper mapper) { + assertEquals( + UNTOUCHED_TEST_VALUE, + mapper.load( + UNTOUCHED_TEST_VALUE.getClass(), + UNTOUCHED_TEST_VALUE.getHashKey(), + UNTOUCHED_TEST_VALUE.getRangeKey())); + assertEquals( + SIGNED_TEST_VALUE, + mapper.load( + SIGNED_TEST_VALUE.getClass(), + SIGNED_TEST_VALUE.getHashKey(), + SIGNED_TEST_VALUE.getRangeKey())); + assertEquals( + MIXED_TEST_VALUE, + mapper.load( + MIXED_TEST_VALUE.getClass(), + MIXED_TEST_VALUE.getHashKey(), + MIXED_TEST_VALUE.getRangeKey())); + assertEquals( + ENCRYPTED_TEST_VALUE, + mapper.load( + ENCRYPTED_TEST_VALUE.getClass(), + ENCRYPTED_TEST_VALUE.getHashKey(), + ENCRYPTED_TEST_VALUE.getRangeKey())); + + assertEquals("Foo", mapper.load(HashKeyOnly.class, "Foo").getHashKey()); + assertEquals("Bar", mapper.load(HashKeyOnly.class, "Bar").getHashKey()); + assertEquals("Baz", mapper.load(HashKeyOnly.class, "Baz").getHashKey()); + + for (int x = 1; x <= 3; ++x) { + KeysOnly obj = mapper.load(KeysOnly.class, 0, x); + assertEquals(0, obj.getHashKey()); + assertEquals(x, obj.getRangeKey()); + + obj = mapper.load(KeysOnly.class, 1, x); + assertEquals(1, obj.getHashKey()); + assertEquals(x, obj.getRangeKey()); + + obj = mapper.load(KeysOnly.class, 4 + x, x); + assertEquals(4 + x, obj.getHashKey()); + assertEquals(x, obj.getRangeKey()); } - - @Test - public void simpleSaveLoadAsym() { - DynamoDBMapper mapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(asymProv)); - - BaseClass obj = new BaseClass(); - obj.setHashKey(0); - obj.setRangeKey(15); - obj.setIntSet(new HashSet()); - obj.getIntSet().add(3); - obj.getIntSet().add(5); - obj.getIntSet().add(7); - obj.setDoubleValue(15); - obj.setStringValue("Blargh!"); - obj.setDoubleSet( - new HashSet(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D))); - mapper.save(obj); - - BaseClass result = mapper.load(BaseClass.class, 0, 15); - assertEquals(obj, result); - - result.setStringValue("Foo"); - mapper.save(result); - - BaseClass result2 = mapper.load(BaseClass.class, 0, 15); - assertEquals(result, result2); - - mapper.delete(result); - assertNull(mapper.load(BaseClass.class, 0, 15)); + } + + private void assertVersionCompatibility_2(DynamoDBMapper mapper) { + assertEquals( + UNTOUCHED_TEST_VALUE_2, + mapper.load( + UNTOUCHED_TEST_VALUE_2.getClass(), + UNTOUCHED_TEST_VALUE_2.getHashKey(), + UNTOUCHED_TEST_VALUE_2.getRangeKey())); + assertEquals( + SIGNED_TEST_VALUE_2, + mapper.load( + SIGNED_TEST_VALUE_2.getClass(), + SIGNED_TEST_VALUE_2.getHashKey(), + SIGNED_TEST_VALUE_2.getRangeKey())); + assertEquals( + MIXED_TEST_VALUE_2, + mapper.load( + MIXED_TEST_VALUE_2.getClass(), + MIXED_TEST_VALUE_2.getHashKey(), + MIXED_TEST_VALUE_2.getRangeKey())); + assertEquals( + ENCRYPTED_TEST_VALUE_2, + mapper.load( + ENCRYPTED_TEST_VALUE_2.getClass(), + ENCRYPTED_TEST_VALUE_2.getHashKey(), + ENCRYPTED_TEST_VALUE_2.getRangeKey())); + + assertEquals("Foo", mapper.load(HashKeyOnly.class, "Foo").getHashKey()); + assertEquals("Bar", mapper.load(HashKeyOnly.class, "Bar").getHashKey()); + assertEquals("Baz", mapper.load(HashKeyOnly.class, "Baz").getHashKey()); + + for (int x = 1; x <= 3; ++x) { + KeysOnly obj = mapper.load(KeysOnly.class, 0, x); + assertEquals(0, obj.getHashKey()); + assertEquals(x, obj.getRangeKey()); + + obj = mapper.load(KeysOnly.class, 1, x); + assertEquals(1, obj.getHashKey()); + assertEquals(x, obj.getRangeKey()); + + obj = mapper.load(KeysOnly.class, 4 + x, x); + assertEquals(4 + x, obj.getHashKey()); + assertEquals(x, obj.getRangeKey()); } - - @Test - public void simpleSaveLoadHashOnly() { - DynamoDBMapper mapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor( - symProv)); - - HashKeyOnly obj = new HashKeyOnly(""); - obj.setHashKey("Foo"); - - mapper.save(obj); - - HashKeyOnly result = mapper.load(HashKeyOnly.class, "Foo"); - assertEquals(obj, result); - - mapper.delete(obj); - assertNull(mapper.load(BaseClass.class, 0, 15)); + } + + // Prints all current tables in the expected test vector format. + // You may need to edit the output to grab the tables you care about, or + // separate the tables into separate files for test vectors (metastores e.g.). + private void printTablesAsTestVectors() throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(AttributeValue.class, new AttributeValueSerializer()); + mapper.registerModule(module); + + Map>> testVector = new HashMap<>(); + for (String table : client.listTables().getTableNames()) { + ScanResult scanResult; + Map lastKey = null; + do { + scanResult = + client.scan(new ScanRequest().withTableName(table).withExclusiveStartKey(lastKey)); + lastKey = scanResult.getLastEvaluatedKey(); + + testVector.put(table, scanResult.getItems()); + + } while (lastKey != null); } - - @Test - public void simpleSaveLoadKeysOnly() { - DynamoDBMapper mapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor( - asymProv)); - - KeysOnly obj = new KeysOnly(); - obj.setHashKey(0); - obj.setRangeKey(15); - - mapper.save(obj); - - KeysOnly result = mapper.load(KeysOnly.class, 0, 15); - assertEquals(obj, result); - - mapper.delete(obj); - assertNull(mapper.load(BaseClass.class, 0, 15)); - } - - public void generateStandardData(EncryptionMaterialsProvider prov) { - DynamoDBMapper mapper = new DynamoDBMapper(client, - new DynamoDBMapperConfig(SaveBehavior.CLOBBER), new AttributeEncryptor(prov)); - mapper.save(new HashKeyOnly("Foo")); - mapper.save(new HashKeyOnly("Bar")); - mapper.save(new HashKeyOnly("Baz")); - - mapper.save(new KeysOnly(0, 1)); - mapper.save(new KeysOnly(0, 2)); - mapper.save(new KeysOnly(0, 3)); - mapper.save(new KeysOnly(1, 1)); - mapper.save(new KeysOnly(1, 2)); - mapper.save(new KeysOnly(1, 3)); - mapper.save(new KeysOnly(5, 1)); - mapper.save(new KeysOnly(6, 2)); - mapper.save(new KeysOnly(7, 3)); - - mapper.save(ENCRYPTED_TEST_VALUE_2); - mapper.save(MIXED_TEST_VALUE_2); - mapper.save(SIGNED_TEST_VALUE_2); - mapper.save(UNTOUCHED_TEST_VALUE_2); - - // Uncomment the function below to print the generated data - // in our test vector format. - - // printTablesAsTestVectors(); - } - - private void assertVersionCompatibility(DynamoDBMapper mapper) { - assertEquals(UNTOUCHED_TEST_VALUE, mapper.load( - UNTOUCHED_TEST_VALUE.getClass(), - UNTOUCHED_TEST_VALUE.getHashKey(), - UNTOUCHED_TEST_VALUE.getRangeKey())); - assertEquals( - SIGNED_TEST_VALUE, - mapper.load(SIGNED_TEST_VALUE.getClass(), - SIGNED_TEST_VALUE.getHashKey(), - SIGNED_TEST_VALUE.getRangeKey())); - assertEquals( - MIXED_TEST_VALUE, - mapper.load(MIXED_TEST_VALUE.getClass(), - MIXED_TEST_VALUE.getHashKey(), - MIXED_TEST_VALUE.getRangeKey())); - assertEquals(ENCRYPTED_TEST_VALUE, mapper.load( - ENCRYPTED_TEST_VALUE.getClass(), - ENCRYPTED_TEST_VALUE.getHashKey(), - ENCRYPTED_TEST_VALUE.getRangeKey())); - - assertEquals("Foo", mapper.load(HashKeyOnly.class, "Foo").getHashKey()); - assertEquals("Bar", mapper.load(HashKeyOnly.class, "Bar").getHashKey()); - assertEquals("Baz", mapper.load(HashKeyOnly.class, "Baz").getHashKey()); - - for (int x = 1; x <= 3; ++x) { - KeysOnly obj = mapper.load(KeysOnly.class, 0, x); - assertEquals(0, obj.getHashKey()); - assertEquals(x, obj.getRangeKey()); - - obj = mapper.load(KeysOnly.class, 1, x); - assertEquals(1, obj.getHashKey()); - assertEquals(x, obj.getRangeKey()); - - obj = mapper.load(KeysOnly.class, 4 + x, x); - assertEquals(4 + x, obj.getHashKey()); - assertEquals(x, obj.getRangeKey()); - } - } - - private void assertVersionCompatibility_2(DynamoDBMapper mapper) { - assertEquals(UNTOUCHED_TEST_VALUE_2, mapper.load( - UNTOUCHED_TEST_VALUE_2.getClass(), - UNTOUCHED_TEST_VALUE_2.getHashKey(), - UNTOUCHED_TEST_VALUE_2.getRangeKey())); - assertEquals( - SIGNED_TEST_VALUE_2, - mapper.load(SIGNED_TEST_VALUE_2.getClass(), - SIGNED_TEST_VALUE_2.getHashKey(), - SIGNED_TEST_VALUE_2.getRangeKey())); - assertEquals( - MIXED_TEST_VALUE_2, - mapper.load(MIXED_TEST_VALUE_2.getClass(), - MIXED_TEST_VALUE_2.getHashKey(), - MIXED_TEST_VALUE_2.getRangeKey())); - assertEquals(ENCRYPTED_TEST_VALUE_2, mapper.load( - ENCRYPTED_TEST_VALUE_2.getClass(), - ENCRYPTED_TEST_VALUE_2.getHashKey(), - ENCRYPTED_TEST_VALUE_2.getRangeKey())); - - assertEquals("Foo", mapper.load(HashKeyOnly.class, "Foo").getHashKey()); - assertEquals("Bar", mapper.load(HashKeyOnly.class, "Bar").getHashKey()); - assertEquals("Baz", mapper.load(HashKeyOnly.class, "Baz").getHashKey()); - - for (int x = 1; x <= 3; ++x) { - KeysOnly obj = mapper.load(KeysOnly.class, 0, x); - assertEquals(0, obj.getHashKey()); - assertEquals(x, obj.getRangeKey()); - - obj = mapper.load(KeysOnly.class, 1, x); - assertEquals(1, obj.getHashKey()); - assertEquals(x, obj.getRangeKey()); - - obj = mapper.load(KeysOnly.class, 4 + x, x); - assertEquals(4 + x, obj.getHashKey()); - assertEquals(x, obj.getRangeKey()); - } - } - - // Prints all current tables in the expected test vector format. - // You may need to edit the output to grab the tables you care about, or - // separate the tables into separate files for test vectors (metastores e.g.). - private void printTablesAsTestVectors() throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addSerializer(AttributeValue.class, new AttributeValueSerializer()); - mapper.registerModule(module); - - Map>> testVector = new HashMap<>(); - for (String table : client.listTables().getTableNames()) { - ScanResult scanResult; - Map lastKey = null; - do { - scanResult = client.scan(new ScanRequest().withTableName(table).withExclusiveStartKey(lastKey)); - lastKey = scanResult.getLastEvaluatedKey(); - - testVector.put(table, scanResult.getItems()); - - } while (lastKey != null); - } - String jsonResult = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(testVector); - System.out.println(jsonResult); - } - - private EncryptionMaterialsProvider createProvider(String providerName, String materialName, Keys keys, ProviderStore metastore) { - switch (providerName) { - case ScenarioManifest.MOST_RECENT_PROVIDER_NAME: - return new CachingMostRecentProvider(metastore, materialName, 1000); - case ScenarioManifest.STATIC_PROVIDER_NAME: - KeyData decryptKeyData = keyDataMap.get(keys.decryptName); - KeyData verifyKeyData = keyDataMap.get(keys.verifyName); - SecretKey decryptKey = new SecretKeySpec(Base64.decode(decryptKeyData.material), decryptKeyData.algorithm); - SecretKey verifyKey = new SecretKeySpec(Base64.decode(verifyKeyData.material), verifyKeyData.algorithm); - return new SymmetricStaticProvider(decryptKey, verifyKey); - case ScenarioManifest.WRAPPED_PROVIDER_NAME: - decryptKeyData = keyDataMap.get(keys.decryptName); - verifyKeyData = keyDataMap.get(keys.verifyName); - - // This can be either the asymmetric provider, where we should test using it's explicit constructor, - // or a wrapped symmetric where we use the wrapped materials constructor. - if (decryptKeyData.keyType.equals(ScenarioManifest.SYMMETRIC_KEY_TYPE)) { - decryptKey = new SecretKeySpec(Base64.decode(decryptKeyData.material), decryptKeyData.algorithm); - verifyKey = new SecretKeySpec(Base64.decode(verifyKeyData.material), verifyKeyData.algorithm); - return new WrappedMaterialsProvider(decryptKey, decryptKey, verifyKey); - } else { - KeyData encryptKeyData = keyDataMap.get(keys.encryptName); - KeyData signKeyData = keyDataMap.get(keys.signName); - try { - // Hardcoded to use RSA for asymmetric keys. If we include vectors with a different - // asymmetric scheme this will need to be updated. - KeyFactory rsaFact = KeyFactory.getInstance(RSA); - - PublicKey encryptMaterial = rsaFact.generatePublic(new X509EncodedKeySpec(Base64 - .decode(encryptKeyData.material))); - PrivateKey decryptMaterial = rsaFact.generatePrivate(new PKCS8EncodedKeySpec(Base64 - .decode(decryptKeyData.material))); - KeyPair decryptPair = new KeyPair(encryptMaterial, decryptMaterial); - - - PublicKey verifyMaterial = rsaFact.generatePublic(new X509EncodedKeySpec(Base64 - .decode(verifyKeyData.material))); - PrivateKey signingMaterial = rsaFact.generatePrivate(new PKCS8EncodedKeySpec(Base64 - .decode(signKeyData.material))); - KeyPair sigPair = new KeyPair(verifyMaterial, signingMaterial); - - return new AsymmetricStaticProvider(decryptPair, sigPair); - } catch (GeneralSecurityException ex) { - throw new RuntimeException(ex); - } - } - case ScenarioManifest.AWS_KMS_PROVIDER_NAME: - return new DirectKmsMaterialProvider(kmsClient, keyDataMap.get(keys.decryptName).keyId); - default: - throw new IllegalStateException("Provider " + providerName + " not yet implemented in test vector runner"); - } - } - - // Create empty tables for the ciphertext. - // The underlying structure to these tables is hardcoded, - // and we run all test vectors assuming the ciphertext matches the key schema for these tables. - private void createCiphertextTables(AmazonDynamoDB client) { - ArrayList attrDef = new ArrayList(); - attrDef.add(new AttributeDefinition().withAttributeName(HASH_KEY).withAttributeType(ScalarAttributeType.N)); - attrDef.add(new AttributeDefinition().withAttributeName(RANGE_KEY).withAttributeType(ScalarAttributeType.N)); - - ArrayList keySchema = new ArrayList(); - keySchema.add(new KeySchemaElement().withAttributeName(HASH_KEY).withKeyType(KeyType.HASH)); - keySchema.add(new KeySchemaElement().withAttributeName(RANGE_KEY).withKeyType(KeyType.RANGE)); - - client.createTable(new CreateTableRequest().withTableName("TableName") - .withAttributeDefinitions(attrDef) - .withKeySchema(keySchema) - .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); - - attrDef = new ArrayList(); - attrDef.add(new AttributeDefinition().withAttributeName(HASH_KEY).withAttributeType(ScalarAttributeType.S)); - keySchema = new ArrayList(); - keySchema.add(new KeySchemaElement().withAttributeName(HASH_KEY).withKeyType(KeyType.HASH)); - - client.createTable(new CreateTableRequest().withTableName("HashKeyOnly") - .withAttributeDefinitions(attrDef) - .withKeySchema(keySchema) - .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); - - attrDef = new ArrayList(); - attrDef.add(new AttributeDefinition().withAttributeName(HASH_KEY).withAttributeType(ScalarAttributeType.B)); - attrDef.add(new AttributeDefinition().withAttributeName(RANGE_KEY).withAttributeType(ScalarAttributeType.N)); - - keySchema = new ArrayList(); - keySchema.add(new KeySchemaElement().withAttributeName(HASH_KEY).withKeyType(KeyType.HASH)); - keySchema.add(new KeySchemaElement().withAttributeName(RANGE_KEY).withKeyType(KeyType.RANGE)); - - client.createTable(new CreateTableRequest().withTableName("DeterministicTable") - .withAttributeDefinitions(attrDef) - .withKeySchema(keySchema) - .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); - } - - // Given a file in the test vector ciphertext format, put those entries into their tables. - // This assumes the expected tables have already been created. - private void putDataFromFile(AmazonDynamoDB client, String filename) throws IOException { - Map>> manifest = getCiphertextManifestFromFile(filename); - for (String tableName : manifest.keySet()) { - for (Map attributes : manifest.get(tableName)) { - client.putItem(new PutItemRequest(tableName, attributes)); - } - } - } - - private Map>> getCiphertextManifestFromFile(String filename) throws IOException { - return getManifestFromFile(TEST_VECTOR_MANIFEST_DIR + stripFilePath(filename), - new TypeReference>>>() {}); - } - - private static T getManifestFromFile(String filename, TypeReference typeRef) throws IOException { - final URL url = TransformerHolisticIT.class.getResource(filename); - if (url == null) { - throw new IllegalStateException("Missing file " + filename + " in src/test/resources."); + String jsonResult = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(testVector); + System.out.println(jsonResult); + } + + private EncryptionMaterialsProvider createProvider( + String providerName, String materialName, Keys keys, ProviderStore metastore) { + switch (providerName) { + case ScenarioManifest.MOST_RECENT_PROVIDER_NAME: + return new CachingMostRecentProvider(metastore, materialName, 1000); + case ScenarioManifest.STATIC_PROVIDER_NAME: + KeyData decryptKeyData = keyDataMap.get(keys.decryptName); + KeyData verifyKeyData = keyDataMap.get(keys.verifyName); + SecretKey decryptKey = + new SecretKeySpec(Base64.decode(decryptKeyData.material), decryptKeyData.algorithm); + SecretKey verifyKey = + new SecretKeySpec(Base64.decode(verifyKeyData.material), verifyKeyData.algorithm); + return new SymmetricStaticProvider(decryptKey, verifyKey); + case ScenarioManifest.WRAPPED_PROVIDER_NAME: + decryptKeyData = keyDataMap.get(keys.decryptName); + verifyKeyData = keyDataMap.get(keys.verifyName); + + // This can be either the asymmetric provider, where we should test using it's explicit + // constructor, + // or a wrapped symmetric where we use the wrapped materials constructor. + if (decryptKeyData.keyType.equals(ScenarioManifest.SYMMETRIC_KEY_TYPE)) { + decryptKey = + new SecretKeySpec(Base64.decode(decryptKeyData.material), decryptKeyData.algorithm); + verifyKey = + new SecretKeySpec(Base64.decode(verifyKeyData.material), verifyKeyData.algorithm); + return new WrappedMaterialsProvider(decryptKey, decryptKey, verifyKey); + } else { + KeyData encryptKeyData = keyDataMap.get(keys.encryptName); + KeyData signKeyData = keyDataMap.get(keys.signName); + try { + // Hardcoded to use RSA for asymmetric keys. If we include vectors with a different + // asymmetric scheme this will need to be updated. + KeyFactory rsaFact = KeyFactory.getInstance(RSA); + + PublicKey encryptMaterial = + rsaFact.generatePublic( + new X509EncodedKeySpec(Base64.decode(encryptKeyData.material))); + PrivateKey decryptMaterial = + rsaFact.generatePrivate( + new PKCS8EncodedKeySpec(Base64.decode(decryptKeyData.material))); + KeyPair decryptPair = new KeyPair(encryptMaterial, decryptMaterial); + + PublicKey verifyMaterial = + rsaFact.generatePublic( + new X509EncodedKeySpec(Base64.decode(verifyKeyData.material))); + PrivateKey signingMaterial = + rsaFact.generatePrivate( + new PKCS8EncodedKeySpec(Base64.decode(signKeyData.material))); + KeyPair sigPair = new KeyPair(verifyMaterial, signingMaterial); + + return new AsymmetricStaticProvider(decryptPair, sigPair); + } catch (GeneralSecurityException ex) { + throw new RuntimeException(ex); + } } - final File manifestFile = new File(url.getPath()); - final ObjectMapper manifestMapper = new ObjectMapper(); - return manifestMapper.readValue( - manifestFile, - typeRef - ); + case ScenarioManifest.AWS_KMS_PROVIDER_NAME: + return new DirectKmsMaterialProvider(kmsClient, keyDataMap.get(keys.decryptName).keyId); + default: + throw new IllegalStateException( + "Provider " + providerName + " not yet implemented in test vector runner"); } - - private static void loadKeyData(String filename) throws IOException { - keyDataMap = getManifestFromFile(TEST_VECTOR_MANIFEST_DIR + stripFilePath(filename), - new TypeReference>() {}); + } + + // Create empty tables for the ciphertext. + // The underlying structure to these tables is hardcoded, + // and we run all test vectors assuming the ciphertext matches the key schema for these tables. + private void createCiphertextTables(AmazonDynamoDB client) { + ArrayList attrDef = new ArrayList(); + attrDef.add( + new AttributeDefinition() + .withAttributeName(HASH_KEY) + .withAttributeType(ScalarAttributeType.N)); + attrDef.add( + new AttributeDefinition() + .withAttributeName(RANGE_KEY) + .withAttributeType(ScalarAttributeType.N)); + + ArrayList keySchema = new ArrayList(); + keySchema.add(new KeySchemaElement().withAttributeName(HASH_KEY).withKeyType(KeyType.HASH)); + keySchema.add(new KeySchemaElement().withAttributeName(RANGE_KEY).withKeyType(KeyType.RANGE)); + + client.createTable( + new CreateTableRequest() + .withTableName("TableName") + .withAttributeDefinitions(attrDef) + .withKeySchema(keySchema) + .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); + + attrDef = new ArrayList(); + attrDef.add( + new AttributeDefinition() + .withAttributeName(HASH_KEY) + .withAttributeType(ScalarAttributeType.S)); + keySchema = new ArrayList(); + keySchema.add(new KeySchemaElement().withAttributeName(HASH_KEY).withKeyType(KeyType.HASH)); + + client.createTable( + new CreateTableRequest() + .withTableName("HashKeyOnly") + .withAttributeDefinitions(attrDef) + .withKeySchema(keySchema) + .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); + + attrDef = new ArrayList(); + attrDef.add( + new AttributeDefinition() + .withAttributeName(HASH_KEY) + .withAttributeType(ScalarAttributeType.B)); + attrDef.add( + new AttributeDefinition() + .withAttributeName(RANGE_KEY) + .withAttributeType(ScalarAttributeType.N)); + + keySchema = new ArrayList(); + keySchema.add(new KeySchemaElement().withAttributeName(HASH_KEY).withKeyType(KeyType.HASH)); + keySchema.add(new KeySchemaElement().withAttributeName(RANGE_KEY).withKeyType(KeyType.RANGE)); + + client.createTable( + new CreateTableRequest() + .withTableName("DeterministicTable") + .withAttributeDefinitions(attrDef) + .withKeySchema(keySchema) + .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L))); + } + + // Given a file in the test vector ciphertext format, put those entries into their tables. + // This assumes the expected tables have already been created. + private void putDataFromFile(AmazonDynamoDB client, String filename) throws IOException { + Map>> manifest = + getCiphertextManifestFromFile(filename); + for (String tableName : manifest.keySet()) { + for (Map attributes : manifest.get(tableName)) { + client.putItem(new PutItemRequest(tableName, attributes)); + } } - - private static String stripFilePath(String path) { - return path.replaceFirst("file://", ""); - } - - @JsonDeserialize(using = AttributeValueDeserializer.class) - public static class DeserializedAttributeValue extends AttributeValue { + } + + private Map>> getCiphertextManifestFromFile( + String filename) throws IOException { + return getManifestFromFile( + TEST_VECTOR_MANIFEST_DIR + stripFilePath(filename), + new TypeReference>>>() {}); + } + + private static T getManifestFromFile(String filename, TypeReference typeRef) + throws IOException { + final URL url = TransformerHolisticIT.class.getResource(filename); + if (url == null) { + throw new IllegalStateException("Missing file " + filename + " in src/test/resources."); } + final File manifestFile = new File(url.getPath()); + final ObjectMapper manifestMapper = new ObjectMapper(); + return manifestMapper.readValue(manifestFile, typeRef); + } + + private static void loadKeyData(String filename) throws IOException { + keyDataMap = + getManifestFromFile( + TEST_VECTOR_MANIFEST_DIR + stripFilePath(filename), + new TypeReference>() {}); + } + + private static String stripFilePath(String path) { + return path.replaceFirst("file://", ""); + } + + @JsonDeserialize(using = AttributeValueDeserializer.class) + public static class DeserializedAttributeValue extends AttributeValue {} } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEncryptionTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEncryptionTest.java index 63cee2c1..d780f4fc 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEncryptionTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEncryptionTest.java @@ -14,19 +14,18 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.SymmetricStaticProvider; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.testing.AttrMatcher; import com.amazonaws.services.dynamodbv2.testing.TestDelegatedKey; -import org.testng.Assert; -import org.testng.AssertJUnit; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.KeyPair; @@ -38,221 +37,256 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.spec.SecretKeySpec; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class DelegatedEncryptionTest { - private static SecretKeySpec rawEncryptionKey; - private static SecretKeySpec rawMacKey; - private static DelegatedKey encryptionKey; - private static DelegatedKey macKey; - - private EncryptionMaterialsProvider prov; - private DynamoDBEncryptor encryptor; - private Map attribs; - private EncryptionContext context; - - @BeforeClass - public static void setupClass() throws Exception { - rawEncryptionKey = new SecretKeySpec(Utils.getRandom(32), "AES"); - encryptionKey = new TestDelegatedKey(rawEncryptionKey); - - rawMacKey = new SecretKeySpec(Utils.getRandom(32), "HmacSHA256"); - macKey = new TestDelegatedKey(rawMacKey); - } - - @BeforeMethod - public void setUp() throws Exception { - prov = new SymmetricStaticProvider(encryptionKey, macKey, - Collections.emptyMap()); - encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); - - attribs = new HashMap(); - attribs.put("intValue", new AttributeValue().withN("123")); - attribs.put("stringValue", new AttributeValue().withS("Hello world!")); - attribs.put("byteArrayValue", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}))); - attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); - attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); - attribs.put("hashKey", new AttributeValue().withN("5")); - attribs.put("rangeKey", new AttributeValue().withN("7")); - attribs.put("version", new AttributeValue().withN("0")); - - context = new EncryptionContext.Builder() - .withTableName("TableName") - .withHashKeyName("hashKey") - .withRangeKeyName("rangeKey") - .build(); - } - - @Test - public void testSetSignatureFieldName() { - assertNotNull(encryptor.getSignatureFieldName()); - encryptor.setSignatureFieldName("A different value"); - assertEquals("A different value", encryptor.getSignatureFieldName()); - } - - @Test - public void testSetMaterialDescriptionFieldName() { - assertNotNull(encryptor.getMaterialDescriptionFieldName()); - encryptor.setMaterialDescriptionFieldName("A different value"); - assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); - } - - @Test - public void fullEncryption() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").getS()); - assertNotNull(encryptedAttributes.get("stringValue").getB()); - } - - @Test(expectedExceptions = SignatureException.class) - public void fullEncryptionBadSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void badVersionNumber() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - ByteBuffer materialDescription = encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).getB(); - byte[] rawArray = materialDescription.array(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getMaterialDescriptionFieldName(), new AttributeValue().withB(ByteBuffer.wrap(rawArray))); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - } - - @Test - public void signedOnly() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() throws GeneralSecurityException { - prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); - encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + private static SecretKeySpec rawEncryptionKey; + private static SecretKeySpec rawMacKey; + private static DelegatedKey encryptionKey; + private static DelegatedKey macKey; + + private EncryptionMaterialsProvider prov; + private DynamoDBEncryptor encryptor; + private Map attribs; + private EncryptionContext context; + + @BeforeClass + public static void setupClass() throws Exception { + rawEncryptionKey = new SecretKeySpec(Utils.getRandom(32), "AES"); + encryptionKey = new TestDelegatedKey(rawEncryptionKey); + + rawMacKey = new SecretKeySpec(Utils.getRandom(32), "HmacSHA256"); + macKey = new TestDelegatedKey(rawMacKey); + } + + @BeforeMethod + public void setUp() throws Exception { + prov = + new SymmetricStaticProvider(encryptionKey, macKey, Collections.emptyMap()); + encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); + + attribs = new HashMap(); + attribs.put("intValue", new AttributeValue().withN("123")); + attribs.put("stringValue", new AttributeValue().withS("Hello world!")); + attribs.put( + "byteArrayValue", + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5}))); + attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); + attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); + attribs.put("hashKey", new AttributeValue().withN("5")); + attribs.put("rangeKey", new AttributeValue().withN("7")); + attribs.put("version", new AttributeValue().withN("0")); + + context = + new EncryptionContext.Builder() + .withTableName("TableName") + .withHashKeyName("hashKey") + .withRangeKeyName("rangeKey") + .build(); + } + + @Test + public void testSetSignatureFieldName() { + assertNotNull(encryptor.getSignatureFieldName()); + encryptor.setSignatureFieldName("A different value"); + assertEquals("A different value", encryptor.getSignatureFieldName()); + } + + @Test + public void testSetMaterialDescriptionFieldName() { + assertNotNull(encryptor.getMaterialDescriptionFieldName()); + encryptor.setMaterialDescriptionFieldName("A different value"); + assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); + } + + @Test + public void fullEncryption() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has been encrypted (we'll assume the others are correct as well) + assertTrue(encryptedAttributes.containsKey("stringValue")); + assertNull(encryptedAttributes.get("stringValue").getS()); + assertNotNull(encryptedAttributes.get("stringValue").getB()); + } + + @Test(expectedExceptions = SignatureException.class) + public void fullEncryptionBadSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void badVersionNumber() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + ByteBuffer materialDescription = + encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).getB(); + byte[] rawArray = materialDescription.array(); + assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. + rawArray[0] = 100; + encryptedAttributes.put( + encryptor.getMaterialDescriptionFieldName(), + new AttributeValue().withB(ByteBuffer.wrap(rawArray))); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + } + + @Test + public void signedOnly() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test + public void signedOnlyNullCryptoKey() throws GeneralSecurityException { + prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); + encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void signedOnlyBadSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test(expectedExceptions = SignatureException.class) + public void signedOnlyNoSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.remove(encryptor.getSignatureFieldName()); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test + public void RsaSignedOnly() throws GeneralSecurityException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + DynamoDBEncryptor.getInstance( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap()), + "encryptor-"); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + DynamoDBEncryptor.getInstance( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap()), + "encryptor-"); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { + Assert.assertEquals(o1.getB(), o2.getB()); + assertSetsEqual(o1.getBS(), o2.getBS()); + Assert.assertEquals(o1.getN(), o2.getN()); + assertSetsEqual(o1.getNS(), o2.getNS()); + Assert.assertEquals(o1.getS(), o2.getS()); + assertSetsEqual(o1.getSS(), o2.getSS()); + } + + private void assertSetsEqual(Collection c1, Collection c2) { + Assert.assertFalse(c1 == null ^ c2 == null); + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + Assert.assertEquals(s1, s2); } - - @Test(expectedExceptions = SignatureException.class) - public void signedOnlyBadSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - @Test(expectedExceptions = SignatureException.class) - public void signedOnlyNoSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - @Test - public void RsaSignedOnly() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = DynamoDBEncryptor.getInstance( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = SignatureException.class) - public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = DynamoDBEncryptor.getInstance( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - Assert.assertEquals(o1.getB(), o2.getB()); - assertSetsEqual(o1.getBS(), o2.getBS()); - Assert.assertEquals(o1.getN(), o2.getN()); - assertSetsEqual(o1.getNS(), o2.getNS()); - Assert.assertEquals(o1.getS(), o2.getS()); - assertSetsEqual(o1.getSS(), o2.getSS()); - } - - private void assertSetsEqual(Collection c1, Collection c2) { - Assert.assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - Assert.assertEquals(s1, s2); - } - } - + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEnvelopeEncryptionTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEnvelopeEncryptionTest.java index 143abdfe..c307b075 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEnvelopeEncryptionTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedEnvelopeEncryptionTest.java @@ -14,6 +14,12 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.SymmetricStaticProvider; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider; @@ -21,13 +27,6 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.testing.AttrMatcher; import com.amazonaws.services.dynamodbv2.testing.TestDelegatedKey; -import org.testng.Assert; -import org.testng.AssertJUnit; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.KeyPair; @@ -39,220 +38,257 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.spec.SecretKeySpec; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class DelegatedEnvelopeEncryptionTest { - private static SecretKeySpec rawEncryptionKey; - private static SecretKeySpec rawMacKey; - private static DelegatedKey encryptionKey; - private static DelegatedKey macKey; - - private EncryptionMaterialsProvider prov; - private DynamoDBEncryptor encryptor; - private Map attribs; - private EncryptionContext context; - - @BeforeClass - public static void setupClass() throws Exception { - rawEncryptionKey = new SecretKeySpec(Utils.getRandom(32), "AES"); - encryptionKey = new TestDelegatedKey(rawEncryptionKey); - - rawMacKey = new SecretKeySpec(Utils.getRandom(32), "HmacSHA256"); - macKey = new TestDelegatedKey(rawMacKey); - } - - @BeforeMethod - public void setUp() throws Exception { - prov = new WrappedMaterialsProvider(encryptionKey, encryptionKey, macKey, Collections.emptyMap()); - encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); - - attribs = new HashMap(); - attribs.put("intValue", new AttributeValue().withN("123")); - attribs.put("stringValue", new AttributeValue().withS("Hello world!")); - attribs.put("byteArrayValue", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}))); - attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); - attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); - attribs.put("hashKey", new AttributeValue().withN("5")); - attribs.put("rangeKey", new AttributeValue().withN("7")); - attribs.put("version", new AttributeValue().withN("0")); - - context = new EncryptionContext.Builder() - .withTableName("TableName") - .withHashKeyName("hashKey") - .withRangeKeyName("rangeKey") - .build(); - } - - @Test - public void testSetSignatureFieldName() { - assertNotNull(encryptor.getSignatureFieldName()); - encryptor.setSignatureFieldName("A different value"); - assertEquals("A different value", encryptor.getSignatureFieldName()); - } - - @Test - public void testSetMaterialDescriptionFieldName() { - assertNotNull(encryptor.getMaterialDescriptionFieldName()); - encryptor.setMaterialDescriptionFieldName("A different value"); - assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); - } - - @Test - public void fullEncryption() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").getS()); - assertNotNull(encryptedAttributes.get("stringValue").getB()); - } - - @Test(expectedExceptions = SignatureException.class) - public void fullEncryptionBadSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void badVersionNumber() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - ByteBuffer materialDescription = encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).getB(); - byte[] rawArray = materialDescription.array(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getMaterialDescriptionFieldName(), new AttributeValue().withB(ByteBuffer.wrap(rawArray))); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - } - - @Test - public void signedOnly() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() throws GeneralSecurityException { - prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); - encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + private static SecretKeySpec rawEncryptionKey; + private static SecretKeySpec rawMacKey; + private static DelegatedKey encryptionKey; + private static DelegatedKey macKey; + + private EncryptionMaterialsProvider prov; + private DynamoDBEncryptor encryptor; + private Map attribs; + private EncryptionContext context; + + @BeforeClass + public static void setupClass() throws Exception { + rawEncryptionKey = new SecretKeySpec(Utils.getRandom(32), "AES"); + encryptionKey = new TestDelegatedKey(rawEncryptionKey); + + rawMacKey = new SecretKeySpec(Utils.getRandom(32), "HmacSHA256"); + macKey = new TestDelegatedKey(rawMacKey); + } + + @BeforeMethod + public void setUp() throws Exception { + prov = + new WrappedMaterialsProvider( + encryptionKey, encryptionKey, macKey, Collections.emptyMap()); + encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); + + attribs = new HashMap(); + attribs.put("intValue", new AttributeValue().withN("123")); + attribs.put("stringValue", new AttributeValue().withS("Hello world!")); + attribs.put( + "byteArrayValue", + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5}))); + attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); + attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); + attribs.put("hashKey", new AttributeValue().withN("5")); + attribs.put("rangeKey", new AttributeValue().withN("7")); + attribs.put("version", new AttributeValue().withN("0")); + + context = + new EncryptionContext.Builder() + .withTableName("TableName") + .withHashKeyName("hashKey") + .withRangeKeyName("rangeKey") + .build(); + } + + @Test + public void testSetSignatureFieldName() { + assertNotNull(encryptor.getSignatureFieldName()); + encryptor.setSignatureFieldName("A different value"); + assertEquals("A different value", encryptor.getSignatureFieldName()); + } + + @Test + public void testSetMaterialDescriptionFieldName() { + assertNotNull(encryptor.getMaterialDescriptionFieldName()); + encryptor.setMaterialDescriptionFieldName("A different value"); + assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); + } + + @Test + public void fullEncryption() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has been encrypted (we'll assume the others are correct as well) + assertTrue(encryptedAttributes.containsKey("stringValue")); + assertNull(encryptedAttributes.get("stringValue").getS()); + assertNotNull(encryptedAttributes.get("stringValue").getB()); + } + + @Test(expectedExceptions = SignatureException.class) + public void fullEncryptionBadSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void badVersionNumber() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + ByteBuffer materialDescription = + encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).getB(); + byte[] rawArray = materialDescription.array(); + assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. + rawArray[0] = 100; + encryptedAttributes.put( + encryptor.getMaterialDescriptionFieldName(), + new AttributeValue().withB(ByteBuffer.wrap(rawArray))); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + } + + @Test + public void signedOnly() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test + public void signedOnlyNullCryptoKey() throws GeneralSecurityException { + prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); + encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void signedOnlyBadSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test(expectedExceptions = SignatureException.class) + public void signedOnlyNoSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.remove(encryptor.getSignatureFieldName()); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test + public void RsaSignedOnly() throws GeneralSecurityException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + DynamoDBEncryptor.getInstance( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap()), + "encryptor-"); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + DynamoDBEncryptor.getInstance( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap()), + "encryptor-"); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { + Assert.assertEquals(o1.getB(), o2.getB()); + assertSetsEqual(o1.getBS(), o2.getBS()); + Assert.assertEquals(o1.getN(), o2.getN()); + assertSetsEqual(o1.getNS(), o2.getNS()); + Assert.assertEquals(o1.getS(), o2.getS()); + assertSetsEqual(o1.getSS(), o2.getSS()); + } + + private void assertSetsEqual(Collection c1, Collection c2) { + Assert.assertFalse(c1 == null ^ c2 == null); + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + Assert.assertEquals(s1, s2); } - - @Test(expectedExceptions = SignatureException.class) - public void signedOnlyBadSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - @Test(expectedExceptions = SignatureException.class) - public void signedOnlyNoSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - @Test - public void RsaSignedOnly() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = DynamoDBEncryptor.getInstance( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = SignatureException.class) - public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = DynamoDBEncryptor.getInstance( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - Assert.assertEquals(o1.getB(), o2.getB()); - assertSetsEqual(o1.getBS(), o2.getBS()); - Assert.assertEquals(o1.getN(), o2.getN()); - assertSetsEqual(o1.getNS(), o2.getNS()); - Assert.assertEquals(o1.getS(), o2.getS()); - assertSetsEqual(o1.getSS(), o2.getSS()); - } - - private void assertSetsEqual(Collection c1, Collection c2) { - Assert.assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - Assert.assertEquals(s1, s2); - } - } - + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index db229798..6c219e52 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -14,6 +14,16 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableName; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.testng.AssertJUnit.assertArrayEquals; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; @@ -21,17 +31,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.testing.AttrMatcher; -import org.bouncycastle.jce.ECNamedCurveTable; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.testng.Assert; -import org.testng.AssertJUnit; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; @@ -50,463 +49,545 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; - -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableName; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.testng.AssertJUnit.assertArrayEquals; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class DynamoDBEncryptorTest { - private static SecretKey encryptionKey; - private static SecretKey macKey; - - private InstrumentedEncryptionMaterialsProvider prov; - private DynamoDBEncryptor encryptor; - private Map attribs; - private EncryptionContext context; - private static final String OVERRIDDEN_TABLE_NAME = "TheBestTableName"; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() throws Exception { - prov = new InstrumentedEncryptionMaterialsProvider( - new SymmetricStaticProvider(encryptionKey, macKey, - Collections.emptyMap())); - encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); - - attribs = new HashMap(); - attribs.put("intValue", new AttributeValue().withN("123")); - attribs.put("stringValue", new AttributeValue().withS("Hello world!")); - attribs.put("byteArrayValue", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}))); - attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); - attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); - attribs.put("hashKey", new AttributeValue().withN("5")); - attribs.put("rangeKey", new AttributeValue().withN("7")); - attribs.put("version", new AttributeValue().withN("0")); - - // New(er) data types - attribs.put("booleanTrue", new AttributeValue().withBOOL(true)); - attribs.put("booleanFalse", new AttributeValue().withBOOL(false)); - attribs.put("nullValue", new AttributeValue().withNULL(true)); - Map tmpMap = new HashMap<>(attribs); - attribs.put("listValue", new AttributeValue().withL( + private static SecretKey encryptionKey; + private static SecretKey macKey; + + private InstrumentedEncryptionMaterialsProvider prov; + private DynamoDBEncryptor encryptor; + private Map attribs; + private EncryptionContext context; + private static final String OVERRIDDEN_TABLE_NAME = "TheBestTableName"; + + @BeforeClass + public static void setUpClass() throws Exception { + KeyGenerator aesGen = KeyGenerator.getInstance("AES"); + aesGen.init(128, Utils.getRng()); + encryptionKey = aesGen.generateKey(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, Utils.getRng()); + macKey = macGen.generateKey(); + } + + @BeforeMethod + public void setUp() throws Exception { + prov = + new InstrumentedEncryptionMaterialsProvider( + new SymmetricStaticProvider( + encryptionKey, macKey, Collections.emptyMap())); + encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); + + attribs = new HashMap(); + attribs.put("intValue", new AttributeValue().withN("123")); + attribs.put("stringValue", new AttributeValue().withS("Hello world!")); + attribs.put( + "byteArrayValue", + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5}))); + attribs.put("stringSet", new AttributeValue().withSS("Goodbye", "Cruel", "World", "?")); + attribs.put("intSet", new AttributeValue().withNS("1", "200", "10", "15", "0")); + attribs.put("hashKey", new AttributeValue().withN("5")); + attribs.put("rangeKey", new AttributeValue().withN("7")); + attribs.put("version", new AttributeValue().withN("0")); + + // New(er) data types + attribs.put("booleanTrue", new AttributeValue().withBOOL(true)); + attribs.put("booleanFalse", new AttributeValue().withBOOL(false)); + attribs.put("nullValue", new AttributeValue().withNULL(true)); + Map tmpMap = new HashMap<>(attribs); + attribs.put( + "listValue", + new AttributeValue() + .withL( new AttributeValue().withS("I'm a string"), new AttributeValue().withN("42"), new AttributeValue().withS("Another string"), new AttributeValue().withNS("1", "4", "7"), new AttributeValue().withM(tmpMap), - new AttributeValue().withL( + new AttributeValue() + .withL( new AttributeValue().withN("123"), new AttributeValue().withNS("1", "200", "10", "15", "0"), - new AttributeValue().withSS("Goodbye", "Cruel", "World", "!") - ))); - tmpMap = new HashMap<>(); - tmpMap.put("another string", new AttributeValue().withS("All around the cobbler's bench")); - tmpMap.put("next line", new AttributeValue().withSS("the monkey", "chased", "the weasel")); - tmpMap.put("more lyrics", new AttributeValue().withL( + new AttributeValue().withSS("Goodbye", "Cruel", "World", "!")))); + tmpMap = new HashMap<>(); + tmpMap.put("another string", new AttributeValue().withS("All around the cobbler's bench")); + tmpMap.put("next line", new AttributeValue().withSS("the monkey", "chased", "the weasel")); + tmpMap.put( + "more lyrics", + new AttributeValue() + .withL( new AttributeValue().withS("the monkey"), new AttributeValue().withS("thought twas"), - new AttributeValue().withS("all in fun") - )); - tmpMap.put("weasel", new AttributeValue().withM(Collections.singletonMap("pop", new AttributeValue().withBOOL(true)))); - attribs.put("song", new AttributeValue().withM(tmpMap)); - - - context = new EncryptionContext.Builder() - .withTableName("TableName") - .withHashKeyName("hashKey") - .withRangeKeyName("rangeKey") - .build(); - } - - @Test - public void testSetSignatureFieldName() { - assertNotNull(encryptor.getSignatureFieldName()); - encryptor.setSignatureFieldName("A different value"); - assertEquals("A different value", encryptor.getSignatureFieldName()); - } - - @Test - public void testSetMaterialDescriptionFieldName() { - assertNotNull(encryptor.getMaterialDescriptionFieldName()); - encryptor.setMaterialDescriptionFieldName("A different value"); - assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); - } - - @Test - public void fullEncryption() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").getS()); - assertNotNull(encryptedAttributes.get("stringValue").getB()); - - // Make sure we're calling the proper getEncryptionMaterials method - assertEquals("Wrong getEncryptionMaterials() called", - 1, prov.getCallCount("getEncryptionMaterials(EncryptionContext context)")); - } - - @Test - public void ensureEncryptedAttributesUnmodified() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - String encryptedString = encryptedAttributes.toString(); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - - assertEquals(encryptedString, encryptedAttributes.toString()); - } - - @Test(expectedExceptions = SignatureException.class) - public void fullEncryptionBadSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void badVersionNumber() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - ByteBuffer materialDescription = encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).getB(); - byte[] rawArray = materialDescription.array(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getMaterialDescriptionFieldName(), new AttributeValue().withB(ByteBuffer.wrap(rawArray))); - encryptor.decryptAllFieldsExcept(Collections.unmodifiableMap(encryptedAttributes), context, "hashKey", "rangeKey", "version"); - } - - @Test - public void signedOnly() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() throws GeneralSecurityException { - prov = new InstrumentedEncryptionMaterialsProvider( - new SymmetricStaticProvider(null, macKey, Collections.emptyMap())); - encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = SignatureException.class) - public void signedOnlyBadSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - @Test(expectedExceptions = SignatureException.class) - public void signedOnlyNoSignature() throws GeneralSecurityException { - Map encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - @Test - public void RsaSignedOnly() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = DynamoDBEncryptor.getInstance( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = SignatureException.class) - public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = DynamoDBEncryptor.getInstance( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - } - - /** - * Tests that no exception is thrown when the encryption context override operator is null - * - * @throws GeneralSecurityException - */ - @Test - public void testNullEncryptionContextOperator() throws GeneralSecurityException { - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); - encryptor.setEncryptionContextOverrideOperator(null); + new AttributeValue().withS("all in fun"))); + tmpMap.put( + "weasel", + new AttributeValue() + .withM(Collections.singletonMap("pop", new AttributeValue().withBOOL(true)))); + attribs.put("song", new AttributeValue().withM(tmpMap)); + + context = + new EncryptionContext.Builder() + .withTableName("TableName") + .withHashKeyName("hashKey") + .withRangeKeyName("rangeKey") + .build(); + } + + @Test + public void testSetSignatureFieldName() { + assertNotNull(encryptor.getSignatureFieldName()); + encryptor.setSignatureFieldName("A different value"); + assertEquals("A different value", encryptor.getSignatureFieldName()); + } + + @Test + public void testSetMaterialDescriptionFieldName() { + assertNotNull(encryptor.getMaterialDescriptionFieldName()); + encryptor.setMaterialDescriptionFieldName("A different value"); + assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); + } + + @Test + public void fullEncryption() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has been encrypted (we'll assume the others are correct as well) + assertTrue(encryptedAttributes.containsKey("stringValue")); + assertNull(encryptedAttributes.get("stringValue").getS()); + assertNotNull(encryptedAttributes.get("stringValue").getB()); + + // Make sure we're calling the proper getEncryptionMaterials method + assertEquals( + "Wrong getEncryptionMaterials() called", + 1, + prov.getCallCount("getEncryptionMaterials(EncryptionContext context)")); + } + + @Test + public void ensureEncryptedAttributesUnmodified() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + String encryptedString = encryptedAttributes.toString(); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + + assertEquals(encryptedString, encryptedAttributes.toString()); + } + + @Test(expectedExceptions = SignatureException.class) + public void fullEncryptionBadSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void badVersionNumber() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept( + Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); + ByteBuffer materialDescription = + encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).getB(); + byte[] rawArray = materialDescription.array(); + assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. + rawArray[0] = 100; + encryptedAttributes.put( + encryptor.getMaterialDescriptionFieldName(), + new AttributeValue().withB(ByteBuffer.wrap(rawArray))); + encryptor.decryptAllFieldsExcept( + Collections.unmodifiableMap(encryptedAttributes), + context, + "hashKey", + "rangeKey", + "version"); + } + + @Test + public void signedOnly() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test + public void signedOnlyNullCryptoKey() throws GeneralSecurityException { + prov = + new InstrumentedEncryptionMaterialsProvider( + new SymmetricStaticProvider(null, macKey, Collections.emptyMap())); + encryptor = DynamoDBEncryptor.getInstance(prov, "encryptor-"); + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void signedOnlyBadSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test(expectedExceptions = SignatureException.class) + public void signedOnlyNoSignature() throws GeneralSecurityException { + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.remove(encryptor.getSignatureFieldName()); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test + public void RsaSignedOnly() throws GeneralSecurityException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + DynamoDBEncryptor.getInstance( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap()), + "encryptor-"); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + encryptor = + DynamoDBEncryptor.getInstance( + new SymmetricStaticProvider( + encryptionKey, sigPair, Collections.emptyMap()), + "encryptor-"); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + /** + * Tests that no exception is thrown when the encryption context override operator is null + * + * @throws GeneralSecurityException + */ + @Test + public void testNullEncryptionContextOperator() throws GeneralSecurityException { + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); + encryptor.setEncryptionContextOverrideOperator(null); + encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); + } + + /** + * Tests decrypt and encrypt with an encryption context override operator + * + * @throws GeneralSecurityException + */ + @Test + public void testTableNameOverriddenEncryptionContextOperator() throws GeneralSecurityException { + // Ensure that the table name is different from what we override the table to. + assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); + encryptor.setEncryptionContextOverrideOperator( + overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); + Map encryptedItems = + encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); + Map decryptedItems = + encryptor.decryptAllFieldsExcept(encryptedItems, context, Collections.emptyList()); + assertThat(decryptedItems, AttrMatcher.match(attribs)); + } + + /** + * Tests encrypt with an encryption context override operator, and a second encryptor without an + * override + * + * @throws GeneralSecurityException + */ + @Test + public void testTableNameOverriddenEncryptionContextOperatorWithSecondEncryptor() + throws GeneralSecurityException { + // Ensure that the table name is different from what we override the table to. + assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); + DynamoDBEncryptor encryptorWithoutOverride = DynamoDBEncryptor.getInstance(prov); + encryptor.setEncryptionContextOverrideOperator( + overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); + Map encryptedItems = encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); - } - - /** - * Tests decrypt and encrypt with an encryption context override operator - * - * @throws GeneralSecurityException - */ - @Test - public void testTableNameOverriddenEncryptionContextOperator() throws GeneralSecurityException { - // Ensure that the table name is different from what we override the table to. - assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); - encryptor.setEncryptionContextOverrideOperator(overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); - Map encryptedItems = encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); - Map decryptedItems = encryptor.decryptAllFieldsExcept(encryptedItems, context, Collections.emptyList()); - assertThat(decryptedItems, AttrMatcher.match(attribs)); - } - - - /** - * Tests encrypt with an encryption context override operator, and a second encryptor without an override - * - * @throws GeneralSecurityException - */ - @Test - public void testTableNameOverriddenEncryptionContextOperatorWithSecondEncryptor() throws GeneralSecurityException { - // Ensure that the table name is different from what we override the table to. - assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); - DynamoDBEncryptor encryptorWithoutOverride = DynamoDBEncryptor.getInstance(prov); - encryptor.setEncryptionContextOverrideOperator(overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); - Map encryptedItems = encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); - - EncryptionContext expectedOverriddenContext = new EncryptionContext.Builder(context).withTableName("TheBestTableName").build(); - Map decryptedItems = encryptorWithoutOverride.decryptAllFieldsExcept(encryptedItems, - expectedOverriddenContext, Collections.emptyList()); - assertThat(decryptedItems, AttrMatcher.match(attribs)); - } - - /** - * Tests encrypt with an encryption context override operator, and a second encryptor without an override - * - * @throws GeneralSecurityException - */ - @Test(expectedExceptions = SignatureException.class) - public void testTableNameOverriddenEncryptionContextOperatorWithSecondEncryptorButTheOriginalEncryptionContext() throws GeneralSecurityException { - // Ensure that the table name is different from what we override the table to. - assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); - DynamoDBEncryptor encryptorWithoutOverride = DynamoDBEncryptor.getInstance(prov); - encryptor.setEncryptionContextOverrideOperator(overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); - Map encryptedItems = encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); - - // Use the original encryption context, and expect a signature failure - Map decryptedItems = encryptorWithoutOverride.decryptAllFieldsExcept(encryptedItems, - context, Collections.emptyList()); - } - - @Test - public void EcdsaSignedOnly() throws GeneralSecurityException { - - encryptor = DynamoDBEncryptor.getInstance(getMaterialProviderwithECDSA()); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + EncryptionContext expectedOverriddenContext = + new EncryptionContext.Builder(context).withTableName("TheBestTableName").build(); + Map decryptedItems = + encryptorWithoutOverride.decryptAllFieldsExcept( + encryptedItems, expectedOverriddenContext, Collections.emptyList()); + assertThat(decryptedItems, AttrMatcher.match(attribs)); + } + + /** + * Tests encrypt with an encryption context override operator, and a second encryptor without an + * override + * + * @throws GeneralSecurityException + */ + @Test(expectedExceptions = SignatureException.class) + public void + testTableNameOverriddenEncryptionContextOperatorWithSecondEncryptorButTheOriginalEncryptionContext() + throws GeneralSecurityException { + // Ensure that the table name is different from what we override the table to. + assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(prov); + DynamoDBEncryptor encryptorWithoutOverride = DynamoDBEncryptor.getInstance(prov); + encryptor.setEncryptionContextOverrideOperator( + overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); + Map encryptedItems = + encryptor.encryptAllFieldsExcept(attribs, context, Collections.emptyList()); - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + // Use the original encryption context, and expect a signature failure + Map decryptedItems = + encryptorWithoutOverride.decryptAllFieldsExcept( + encryptedItems, context, Collections.emptyList()); + } + + @Test + public void EcdsaSignedOnly() throws GeneralSecurityException { + + encryptor = DynamoDBEncryptor.getInstance(getMaterialProviderwithECDSA()); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + Map decryptedAttributes = + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + + // Make sure keys and version are not encrypted + assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); + assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); + assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); + + // Make sure String has not been encrypted (we'll assume the others are correct as well) + assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); + } + + @Test(expectedExceptions = SignatureException.class) + public void EcdsaSignedOnlyBadSignature() throws GeneralSecurityException { + + encryptor = DynamoDBEncryptor.getInstance(getMaterialProviderwithECDSA()); + + Map encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.get("hashKey").setN("666"); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test + public void toByteArray() throws ReflectiveOperationException { + final byte[] expected = new byte[] {0, 1, 2, 3, 4, 5}; + assertToByteArray("Wrap", expected, ByteBuffer.wrap(expected)); + assertToByteArray("Wrap-RO", expected, ByteBuffer.wrap(expected).asReadOnlyBuffer()); + + assertToByteArray( + "Wrap-Truncated-Sliced", + expected, + ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 6).slice()); + assertToByteArray( + "Wrap-Offset-Sliced", + expected, + ByteBuffer.wrap(new byte[] {6, 0, 1, 2, 3, 4, 5, 6}, 1, 6).slice()); + assertToByteArray( + "Wrap-Truncated", expected, ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 6)); + assertToByteArray( + "Wrap-Offset", expected, ByteBuffer.wrap(new byte[] {6, 0, 1, 2, 3, 4, 5, 6}, 1, 6)); + + ByteBuffer buff = ByteBuffer.allocate(expected.length + 10); + buff.put(expected); + buff.flip(); + assertToByteArray("Normal", expected, buff); + + buff = ByteBuffer.allocateDirect(expected.length + 10); + buff.put(expected); + buff.flip(); + assertToByteArray("Direct", expected, buff); + } + + private void assertToByteArray( + final String msg, final byte[] expected, final ByteBuffer testValue) + throws ReflectiveOperationException { + Method m = DynamoDBEncryptor.class.getDeclaredMethod("toByteArray", ByteBuffer.class); + m.setAccessible(true); + + int oldPosition = testValue.position(); + int oldLimit = testValue.limit(); + + assertArrayEquals(msg + ":Array", expected, (byte[]) m.invoke(null, testValue)); + assertEquals(msg + ":Position", oldPosition, testValue.position()); + assertEquals(msg + ":Limit", oldLimit, testValue.limit()); + } + + private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { + Assert.assertEquals(o1.getB(), o2.getB()); + assertSetsEqual(o1.getBS(), o2.getBS()); + Assert.assertEquals(o1.getN(), o2.getN()); + assertSetsEqual(o1.getNS(), o2.getNS()); + Assert.assertEquals(o1.getS(), o2.getS()); + assertSetsEqual(o1.getSS(), o2.getSS()); + } + + private void assertSetsEqual(Collection c1, Collection c2) { + Assert.assertFalse(c1 == null ^ c2 == null); + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + Assert.assertEquals(s1, s2); } - - @Test(expectedExceptions = SignatureException.class) - public void EcdsaSignedOnlyBadSignature() throws GeneralSecurityException { - - encryptor = DynamoDBEncryptor.getInstance(getMaterialProviderwithECDSA()); - - Map encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0])); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.get("hashKey").setN("666"); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + private EncryptionMaterialsProvider getMaterialProviderwithECDSA() + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException { + Security.addProvider(new BouncyCastleProvider()); + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1"); + KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); + g.initialize(ecSpec, Utils.getRng()); + KeyPair keypair = g.generateKeyPair(); + Map description = new HashMap(); + description.put(DynamoDBEncryptor.DEFAULT_SIGNING_ALGORITHM_HEADER, "SHA384withECDSA"); + return new SymmetricStaticProvider(null, keypair, description); + } + + private static final class InstrumentedEncryptionMaterialsProvider + implements EncryptionMaterialsProvider { + private final EncryptionMaterialsProvider delegate; + private final ConcurrentHashMap calls = new ConcurrentHashMap<>(); + + public InstrumentedEncryptionMaterialsProvider(EncryptionMaterialsProvider delegate) { + this.delegate = delegate; } - @Test - public void toByteArray() throws ReflectiveOperationException { - final byte[] expected = new byte[]{0, 1, 2, 3, 4, 5}; - assertToByteArray("Wrap", expected, ByteBuffer.wrap(expected)); - assertToByteArray("Wrap-RO", expected, ByteBuffer.wrap(expected).asReadOnlyBuffer()); - - assertToByteArray("Wrap-Truncated-Sliced", expected, ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6}, 0, 6).slice()); - assertToByteArray("Wrap-Offset-Sliced", expected, ByteBuffer.wrap(new byte[]{6, 0, 1, 2, 3, 4, 5, 6}, 1, 6).slice()); - assertToByteArray("Wrap-Truncated", expected, ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6}, 0, 6)); - assertToByteArray("Wrap-Offset", expected, ByteBuffer.wrap(new byte[]{6, 0, 1, 2, 3, 4, 5, 6}, 1, 6)); - - ByteBuffer buff = ByteBuffer.allocate(expected.length + 10); - buff.put(expected); - buff.flip(); - assertToByteArray("Normal", expected, buff); - - buff = ByteBuffer.allocateDirect(expected.length + 10); - buff.put(expected); - buff.flip(); - assertToByteArray("Direct", expected, buff); + @Override + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + incrementMethodCount("getDecryptionMaterials()"); + return delegate.getDecryptionMaterials(context); } - private void assertToByteArray(final String msg, final byte[] expected, final ByteBuffer testValue) throws ReflectiveOperationException { - Method m = DynamoDBEncryptor.class.getDeclaredMethod("toByteArray", ByteBuffer.class); - m.setAccessible(true); - - int oldPosition = testValue.position(); - int oldLimit = testValue.limit(); - - assertArrayEquals(msg + ":Array", expected, (byte[]) m.invoke(null, testValue)); - assertEquals(msg + ":Position", oldPosition, testValue.position()); - assertEquals(msg + ":Limit", oldLimit, testValue.limit()); + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + incrementMethodCount("getEncryptionMaterials(EncryptionContext context)"); + return delegate.getEncryptionMaterials(context); } - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - Assert.assertEquals(o1.getB(), o2.getB()); - assertSetsEqual(o1.getBS(), o2.getBS()); - Assert.assertEquals(o1.getN(), o2.getN()); - assertSetsEqual(o1.getNS(), o2.getNS()); - Assert.assertEquals(o1.getS(), o2.getS()); - assertSetsEqual(o1.getSS(), o2.getSS()); + @Override + public void refresh() { + incrementMethodCount("refresh()"); + delegate.refresh(); } - private void assertSetsEqual(Collection c1, Collection c2) { - Assert.assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - Assert.assertEquals(s1, s2); - } + public int getCallCount(String method) { + AtomicInteger count = calls.get(method); + if (count != null) { + return count.intValue(); + } else { + return 0; + } } - private EncryptionMaterialsProvider getMaterialProviderwithECDSA() - throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException { - Security.addProvider(new BouncyCastleProvider()); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1"); - KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); - g.initialize(ecSpec, Utils.getRng()); - KeyPair keypair = g.generateKeyPair(); - Map description = new HashMap(); - description.put(DynamoDBEncryptor.DEFAULT_SIGNING_ALGORITHM_HEADER, "SHA384withECDSA"); - return new SymmetricStaticProvider(null, keypair, description); + @SuppressWarnings("unused") + public void resetCallCounts() { + calls.clear(); } - private static final class InstrumentedEncryptionMaterialsProvider implements EncryptionMaterialsProvider { - private final EncryptionMaterialsProvider delegate; - private final ConcurrentHashMap calls = new ConcurrentHashMap<>(); - - public InstrumentedEncryptionMaterialsProvider(EncryptionMaterialsProvider delegate) { - this.delegate = delegate; - } - - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - incrementMethodCount("getDecryptionMaterials()"); - return delegate.getDecryptionMaterials(context); - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - incrementMethodCount("getEncryptionMaterials(EncryptionContext context)"); - return delegate.getEncryptionMaterials(context); - } - - @Override - public void refresh() { - incrementMethodCount("refresh()"); - delegate.refresh(); - } - - public int getCallCount(String method) { - AtomicInteger count = calls.get(method); - if (count != null) { - return count.intValue(); - } else { - return 0; - } - } - - @SuppressWarnings("unused") - public void resetCallCounts() { - calls.clear(); - } - - private void incrementMethodCount(String method) { - AtomicInteger oldValue = calls.putIfAbsent(method, new AtomicInteger(1)); - if (oldValue != null) { - oldValue.incrementAndGet(); - } - } + private void incrementMethodCount(String method) { + AtomicInteger oldValue = calls.putIfAbsent(method, new AtomicInteger(1)); + if (oldValue != null) { + oldValue.incrementAndGet(); + } } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSignerTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSignerTest.java index 116c6a6c..b7422c89 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSignerTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSignerTest.java @@ -16,14 +16,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; import com.amazonaws.services.dynamodbv2.model.AttributeValue; -import org.bouncycastle.jce.ECNamedCurveTable; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.Key; @@ -35,401 +27,491 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import javax.crypto.KeyGenerator; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class DynamoDBSignerTest { - // These use the Key type (rather than PublicKey, PrivateKey, and SecretKey) - // to test the routing logic within the signer. - private static Key pubKeyRsa; - private static Key privKeyRsa; - private static Key macKey; - private DynamoDBSigner signerRsa; - private DynamoDBSigner signerEcdsa; - private static Key pubKeyEcdsa; - private static Key privKeyEcdsa; - - @BeforeClass - public static void setUpClass() throws Exception { - - //RSA key generation - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - pubKeyRsa = sigPair.getPublic(); - privKeyRsa = sigPair.getPrivate(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - - Security.addProvider(new BouncyCastleProvider()); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1"); - KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); - g.initialize(ecSpec, Utils.getRng()); - KeyPair keypair = g.generateKeyPair(); - pubKeyEcdsa = keypair.getPublic(); - privKeyEcdsa = keypair.getPrivate(); - - } - - @BeforeMethod - public void setUp() { - signerRsa = DynamoDBSigner.getInstance("SHA256withRSA", Utils.getRng()); - signerEcdsa = DynamoDBSigner.getInstance("SHA384withECDSA", Utils.getRng()); - } - - @Test - public void mac() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macLists() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withSS("Value1", "Value2", "Value3")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withNS("100", "200", "300")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withBS(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}), - ByteBuffer.wrap(new byte[]{3, 2, 1}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macListsUnsorted() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withSS("Value3", "Value1", "Value2")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withNS("100", "300", "200")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withBS(ByteBuffer.wrap(new byte[]{3, 2, 1}), - ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - Map scrambledAttributes = new HashMap(); - scrambledAttributes.put("Key1", new AttributeValue().withSS("Value1", "Value2", "Value3")); - scrambledAttributes.put("Key2", new AttributeValue().withNS("100", "200", "300")); - scrambledAttributes.put("Key3", new AttributeValue().withBS(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}), - ByteBuffer.wrap(new byte[]{3, 2, 1}))); - - signerRsa.verifySignature(scrambledAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macNoAdMatchesEmptyAd() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, null, macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macWithIgnoredChange() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - itemAttributes.put("Key4", new AttributeValue().withS("Ignored Value")); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - - itemAttributes.put("Key4", new AttributeValue().withS("New Ignored Value")); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void macChangedValue() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - itemAttributes.get("Key2").setN("99"); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void macChangedFlag() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void macChangedAssociatedData() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[]{3, 2, 1}, macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[]{1, 2, 3}, macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void sig() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigWithReadOnlySignature() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature).asReadOnlyBuffer()); - } - - @Test - public void sigNoAdMatchesEmptyAd() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, null, privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigWithIgnoredChange() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - itemAttributes.put("Key4", new AttributeValue().withS("Ignored Value")); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - itemAttributes.put("Key4", new AttributeValue().withS("New Ignored Value")); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigChangedValue() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - itemAttributes.get("Key2").setN("99"); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigChangedFlag() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigChangedAssociatedData() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[]{1, 2, 3}, pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigEcdsa() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigEcdsaWithReadOnlySignature() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature).asReadOnlyBuffer()); - } - - @Test - public void sigEcdsaNoAdMatchesEmptyAd() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, null, privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigEcdsaWithIgnoredChange() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key4", new AttributeValue().withS("Ignored Value")); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - itemAttributes.put("Key4", new AttributeValue().withS("New Ignored Value")); - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigEcdsaChangedValue() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - itemAttributes.get("Key2").setN("99"); - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigEcdsaChangedAssociatedData() throws GeneralSecurityException { - Map itemAttributes = new HashMap(); - Map> attributeFlags = new HashMap>(); - - itemAttributes.put("Key1", new AttributeValue().withS("Value1")); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", new AttributeValue().withN("100")); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3}))); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[]{1, 2, 3}, pubKeyEcdsa, ByteBuffer.wrap(signature)); - } + // These use the Key type (rather than PublicKey, PrivateKey, and SecretKey) + // to test the routing logic within the signer. + private static Key pubKeyRsa; + private static Key privKeyRsa; + private static Key macKey; + private DynamoDBSigner signerRsa; + private DynamoDBSigner signerEcdsa; + private static Key pubKeyEcdsa; + private static Key privKeyEcdsa; + + @BeforeClass + public static void setUpClass() throws Exception { + + // RSA key generation + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + KeyPair sigPair = rsaGen.generateKeyPair(); + pubKeyRsa = sigPair.getPublic(); + privKeyRsa = sigPair.getPrivate(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, Utils.getRng()); + macKey = macGen.generateKey(); + + Security.addProvider(new BouncyCastleProvider()); + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1"); + KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); + g.initialize(ecSpec, Utils.getRng()); + KeyPair keypair = g.generateKeyPair(); + pubKeyEcdsa = keypair.getPublic(); + privKeyEcdsa = keypair.getPrivate(); + } + + @BeforeMethod + public void setUp() { + signerRsa = DynamoDBSigner.getInstance("SHA256withRSA", Utils.getRng()); + signerEcdsa = DynamoDBSigner.getInstance("SHA384withECDSA", Utils.getRng()); + } + + @Test + public void mac() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); + + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test + public void macLists() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withSS("Value1", "Value2", "Value3")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withNS("100", "200", "300")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", + new AttributeValue() + .withBS( + ByteBuffer.wrap(new byte[] {0, 1, 2, 3}), ByteBuffer.wrap(new byte[] {3, 2, 1}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); + + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test + public void macListsUnsorted() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withSS("Value3", "Value1", "Value2")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withNS("100", "300", "200")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", + new AttributeValue() + .withBS( + ByteBuffer.wrap(new byte[] {3, 2, 1}), ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); + + Map scrambledAttributes = new HashMap(); + scrambledAttributes.put("Key1", new AttributeValue().withSS("Value1", "Value2", "Value3")); + scrambledAttributes.put("Key2", new AttributeValue().withNS("100", "200", "300")); + scrambledAttributes.put( + "Key3", + new AttributeValue() + .withBS( + ByteBuffer.wrap(new byte[] {0, 1, 2, 3}), ByteBuffer.wrap(new byte[] {3, 2, 1}))); + + signerRsa.verifySignature( + scrambledAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test + public void macNoAdMatchesEmptyAd() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, null, macKey); + + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test + public void macWithIgnoredChange() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + itemAttributes.put("Key4", new AttributeValue().withS("Ignored Value")); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); + + itemAttributes.put("Key4", new AttributeValue().withS("New Ignored Value")); + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void macChangedValue() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); + + itemAttributes.get("Key2").setN("99"); + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void macChangedFlag() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); + + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void macChangedAssociatedData() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[] {3, 2, 1}, macKey); + + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[] {1, 2, 3}, macKey, ByteBuffer.wrap(signature)); + } + + @Test + public void sig() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); + + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); + } + + @Test + public void sigWithReadOnlySignature() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); + + signerRsa.verifySignature( + itemAttributes, + attributeFlags, + new byte[0], + pubKeyRsa, + ByteBuffer.wrap(signature).asReadOnlyBuffer()); + } + + @Test + public void sigNoAdMatchesEmptyAd() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, null, privKeyRsa); + + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); + } + + @Test + public void sigWithIgnoredChange() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + itemAttributes.put("Key4", new AttributeValue().withS("Ignored Value")); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); + + itemAttributes.put("Key4", new AttributeValue().withS("New Ignored Value")); + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void sigChangedValue() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); + + itemAttributes.get("Key2").setN("99"); + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void sigChangedFlag() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); + + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + signerRsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void sigChangedAssociatedData() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); + byte[] signature = + signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); + + signerRsa.verifySignature( + itemAttributes, + attributeFlags, + new byte[] {1, 2, 3}, + pubKeyRsa, + ByteBuffer.wrap(signature)); + } + + @Test + public void sigEcdsa() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + byte[] signature = + signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); + + signerEcdsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); + } + + @Test + public void sigEcdsaWithReadOnlySignature() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + byte[] signature = + signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); + + signerEcdsa.verifySignature( + itemAttributes, + attributeFlags, + new byte[0], + pubKeyEcdsa, + ByteBuffer.wrap(signature).asReadOnlyBuffer()); + } + + @Test + public void sigEcdsaNoAdMatchesEmptyAd() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + byte[] signature = + signerEcdsa.calculateSignature(itemAttributes, attributeFlags, null, privKeyEcdsa); + + signerEcdsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); + } + + @Test + public void sigEcdsaWithIgnoredChange() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key4", new AttributeValue().withS("Ignored Value")); + byte[] signature = + signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); + + itemAttributes.put("Key4", new AttributeValue().withS("New Ignored Value")); + signerEcdsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void sigEcdsaChangedValue() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + byte[] signature = + signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); + + itemAttributes.get("Key2").setN("99"); + signerEcdsa.verifySignature( + itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); + } + + @Test(expectedExceptions = SignatureException.class) + public void sigEcdsaChangedAssociatedData() throws GeneralSecurityException { + Map itemAttributes = new HashMap(); + Map> attributeFlags = new HashMap>(); + + itemAttributes.put("Key1", new AttributeValue().withS("Value1")); + attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put("Key2", new AttributeValue().withN("100")); + attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); + itemAttributes.put( + "Key3", new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}))); + attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); + byte[] signature = + signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); + + signerEcdsa.verifySignature( + itemAttributes, + attributeFlags, + new byte[] {1, 2, 3}, + pubKeyEcdsa, + ByteBuffer.wrap(signature)); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterialsTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterialsTest.java index 56e9e222..4594f0d7 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterialsTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterialsTest.java @@ -14,12 +14,9 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -27,105 +24,113 @@ import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class AsymmetricRawMaterialsTest { - private static SecureRandom rnd; - private static KeyPair encryptionPair; - private static SecretKey macKey; - private static KeyPair sigPair; - private Map description; - - @BeforeClass - public static void setUpClass() throws NoSuchAlgorithmException { - rnd = new SecureRandom(); - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, rnd); - encryptionPair = rsaGen.generateKeyPair(); - sigPair = rsaGen.generateKeyPair(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, rnd); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap(); - description.put("TestKey", "test value"); - } - - @Test - public void macNoDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, macKey); - assertEquals(macKey, matEncryption.getSigningKey()); - assertEquals(macKey, matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, macKey, matEncryption.getMaterialDescription()); - assertEquals(macKey, matDecryption.getSigningKey()); - assertEquals(macKey, matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - } - - @Test - public void macWithDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, macKey, description); - assertEquals(macKey, matEncryption.getSigningKey()); - assertEquals(macKey, matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - assertEquals("test value", matEncryption.getMaterialDescription().get("TestKey")); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, macKey, matEncryption.getMaterialDescription()); - assertEquals(macKey, matDecryption.getSigningKey()); - assertEquals(macKey, matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - assertEquals("test value", matDecryption.getMaterialDescription().get("TestKey")); - } - - @Test - public void sigNoDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, sigPair); - assertEquals(sigPair.getPrivate(), matEncryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, sigPair, matEncryption.getMaterialDescription()); - assertEquals(sigPair.getPrivate(), matDecryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - } - - @Test - public void sigWithDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, sigPair, description); - assertEquals(sigPair.getPrivate(), matEncryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - assertEquals("test value", matEncryption.getMaterialDescription().get("TestKey")); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, sigPair, matEncryption.getMaterialDescription()); - assertEquals(sigPair.getPrivate(), matDecryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - assertEquals("test value", matDecryption.getMaterialDescription().get("TestKey")); - } + private static SecureRandom rnd; + private static KeyPair encryptionPair; + private static SecretKey macKey; + private static KeyPair sigPair; + private Map description; + + @BeforeClass + public static void setUpClass() throws NoSuchAlgorithmException { + rnd = new SecureRandom(); + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, rnd); + encryptionPair = rsaGen.generateKeyPair(); + sigPair = rsaGen.generateKeyPair(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, rnd); + macKey = macGen.generateKey(); + } + + @BeforeMethod + public void setUp() { + description = new HashMap(); + description.put("TestKey", "test value"); + } + + @Test + public void macNoDescription() throws GeneralSecurityException { + AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, macKey); + assertEquals(macKey, matEncryption.getSigningKey()); + assertEquals(macKey, matEncryption.getVerificationKey()); + assertFalse(matEncryption.getMaterialDescription().isEmpty()); + + SecretKey envelopeKey = matEncryption.getEncryptionKey(); + assertEquals(envelopeKey, matEncryption.getDecryptionKey()); + + AsymmetricRawMaterials matDecryption = + new AsymmetricRawMaterials(encryptionPair, macKey, matEncryption.getMaterialDescription()); + assertEquals(macKey, matDecryption.getSigningKey()); + assertEquals(macKey, matDecryption.getVerificationKey()); + assertEquals(envelopeKey, matDecryption.getEncryptionKey()); + assertEquals(envelopeKey, matDecryption.getDecryptionKey()); + } + + @Test + public void macWithDescription() throws GeneralSecurityException { + AsymmetricRawMaterials matEncryption = + new AsymmetricRawMaterials(encryptionPair, macKey, description); + assertEquals(macKey, matEncryption.getSigningKey()); + assertEquals(macKey, matEncryption.getVerificationKey()); + assertFalse(matEncryption.getMaterialDescription().isEmpty()); + assertEquals("test value", matEncryption.getMaterialDescription().get("TestKey")); + + SecretKey envelopeKey = matEncryption.getEncryptionKey(); + assertEquals(envelopeKey, matEncryption.getDecryptionKey()); + + AsymmetricRawMaterials matDecryption = + new AsymmetricRawMaterials(encryptionPair, macKey, matEncryption.getMaterialDescription()); + assertEquals(macKey, matDecryption.getSigningKey()); + assertEquals(macKey, matDecryption.getVerificationKey()); + assertEquals(envelopeKey, matDecryption.getEncryptionKey()); + assertEquals(envelopeKey, matDecryption.getDecryptionKey()); + assertEquals("test value", matDecryption.getMaterialDescription().get("TestKey")); + } + + @Test + public void sigNoDescription() throws GeneralSecurityException { + AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, sigPair); + assertEquals(sigPair.getPrivate(), matEncryption.getSigningKey()); + assertEquals(sigPair.getPublic(), matEncryption.getVerificationKey()); + assertFalse(matEncryption.getMaterialDescription().isEmpty()); + + SecretKey envelopeKey = matEncryption.getEncryptionKey(); + assertEquals(envelopeKey, matEncryption.getDecryptionKey()); + + AsymmetricRawMaterials matDecryption = + new AsymmetricRawMaterials(encryptionPair, sigPair, matEncryption.getMaterialDescription()); + assertEquals(sigPair.getPrivate(), matDecryption.getSigningKey()); + assertEquals(sigPair.getPublic(), matDecryption.getVerificationKey()); + assertEquals(envelopeKey, matDecryption.getEncryptionKey()); + assertEquals(envelopeKey, matDecryption.getDecryptionKey()); + } + + @Test + public void sigWithDescription() throws GeneralSecurityException { + AsymmetricRawMaterials matEncryption = + new AsymmetricRawMaterials(encryptionPair, sigPair, description); + assertEquals(sigPair.getPrivate(), matEncryption.getSigningKey()); + assertEquals(sigPair.getPublic(), matEncryption.getVerificationKey()); + assertFalse(matEncryption.getMaterialDescription().isEmpty()); + assertEquals("test value", matEncryption.getMaterialDescription().get("TestKey")); + + SecretKey envelopeKey = matEncryption.getEncryptionKey(); + assertEquals(envelopeKey, matEncryption.getDecryptionKey()); + + AsymmetricRawMaterials matDecryption = + new AsymmetricRawMaterials(encryptionPair, sigPair, matEncryption.getMaterialDescription()); + assertEquals(sigPair.getPrivate(), matDecryption.getSigningKey()); + assertEquals(sigPair.getPublic(), matDecryption.getVerificationKey()); + assertEquals(envelopeKey, matDecryption.getEncryptionKey()); + assertEquals(envelopeKey, matDecryption.getDecryptionKey()); + assertEquals("test value", matDecryption.getMaterialDescription().get("TestKey")); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterialsTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterialsTest.java index 4c71690c..4cf09f5e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterialsTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterialsTest.java @@ -14,90 +14,89 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class SymmetricRawMaterialsTest { - private static SecretKey encryptionKey; - private static SecretKey macKey; - private static KeyPair sigPair; - private static SecureRandom rnd; - private Map description; + private static SecretKey encryptionKey; + private static SecretKey macKey; + private static KeyPair sigPair; + private static SecureRandom rnd; + private Map description; - @BeforeClass - public static void setUpClass() throws NoSuchAlgorithmException { - rnd = new SecureRandom(); - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, rnd); - sigPair = rsaGen.generateKeyPair(); + @BeforeClass + public static void setUpClass() throws NoSuchAlgorithmException { + rnd = new SecureRandom(); + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, rnd); + sigPair = rsaGen.generateKeyPair(); - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, rnd); - encryptionKey = aesGen.generateKey(); + KeyGenerator aesGen = KeyGenerator.getInstance("AES"); + aesGen.init(128, rnd); + encryptionKey = aesGen.generateKey(); - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, rnd); - macKey = macGen.generateKey(); - } + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, rnd); + macKey = macGen.generateKey(); + } - @BeforeMethod - public void setUp() { - description = new HashMap(); - description.put("TestKey", "test value"); - } + @BeforeMethod + public void setUp() { + description = new HashMap(); + description.put("TestKey", "test value"); + } - @Test - public void macNoDescription() throws NoSuchAlgorithmException { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, macKey); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(macKey, mat.getSigningKey()); - assertEquals(macKey, mat.getVerificationKey()); - assertTrue(mat.getMaterialDescription().isEmpty()); - } + @Test + public void macNoDescription() throws NoSuchAlgorithmException { + SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, macKey); + assertEquals(encryptionKey, mat.getEncryptionKey()); + assertEquals(encryptionKey, mat.getDecryptionKey()); + assertEquals(macKey, mat.getSigningKey()); + assertEquals(macKey, mat.getVerificationKey()); + assertTrue(mat.getMaterialDescription().isEmpty()); + } - @Test - public void macWithDescription() throws NoSuchAlgorithmException { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, macKey, description); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(macKey, mat.getSigningKey()); - assertEquals(macKey, mat.getVerificationKey()); - assertEquals(description, mat.getMaterialDescription()); - assertEquals("test value", mat.getMaterialDescription().get("TestKey")); - } + @Test + public void macWithDescription() throws NoSuchAlgorithmException { + SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, macKey, description); + assertEquals(encryptionKey, mat.getEncryptionKey()); + assertEquals(encryptionKey, mat.getDecryptionKey()); + assertEquals(macKey, mat.getSigningKey()); + assertEquals(macKey, mat.getVerificationKey()); + assertEquals(description, mat.getMaterialDescription()); + assertEquals("test value", mat.getMaterialDescription().get("TestKey")); + } - @Test - public void sigNoDescription() throws NoSuchAlgorithmException { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, sigPair); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(sigPair.getPrivate(), mat.getSigningKey()); - assertEquals(sigPair.getPublic(), mat.getVerificationKey()); - assertTrue(mat.getMaterialDescription().isEmpty()); - } + @Test + public void sigNoDescription() throws NoSuchAlgorithmException { + SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, sigPair); + assertEquals(encryptionKey, mat.getEncryptionKey()); + assertEquals(encryptionKey, mat.getDecryptionKey()); + assertEquals(sigPair.getPrivate(), mat.getSigningKey()); + assertEquals(sigPair.getPublic(), mat.getVerificationKey()); + assertTrue(mat.getMaterialDescription().isEmpty()); + } - @Test - public void sigWithDescription() throws NoSuchAlgorithmException { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, sigPair, description); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(sigPair.getPrivate(), mat.getSigningKey()); - assertEquals(sigPair.getPublic(), mat.getVerificationKey()); - assertEquals(description, mat.getMaterialDescription()); - assertEquals("test value", mat.getMaterialDescription().get("TestKey")); - } + @Test + public void sigWithDescription() throws NoSuchAlgorithmException { + SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, sigPair, description); + assertEquals(encryptionKey, mat.getEncryptionKey()); + assertEquals(encryptionKey, mat.getDecryptionKey()); + assertEquals(sigPair.getPrivate(), mat.getSigningKey()); + assertEquals(sigPair.getPublic(), mat.getVerificationKey()); + assertEquals(description, mat.getMaterialDescription()); + assertEquals("test value", mat.getMaterialDescription().get("TestKey")); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProviderTest.java index 7d90f904..42eb1fad 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProviderTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/AsymmetricStaticProviderTest.java @@ -14,108 +14,114 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class AsymmetricStaticProviderTest { - private static KeyPair encryptionPair; - private static SecretKey macKey; - private static KeyPair sigPair; - private Map description; - private EncryptionContext ctx; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - sigPair = rsaGen.generateKeyPair(); - encryptionPair = rsaGen.generateKeyPair(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = new EncryptionContext.Builder().build(); - } - - @Test - public void constructWithMac() throws GeneralSecurityException { - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, macKey, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void constructWithSigPair() throws GeneralSecurityException { - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void randomEnvelopeKeys() throws GeneralSecurityException { - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, macKey, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(macKey, eMat.getSigningKey()); - - EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey2 = eMat2.getEncryptionKey(); - assertEquals(macKey, eMat.getSigningKey()); - - assertFalse("Envelope keys must be different", encryptionKey.equals(encryptionKey2)); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw and exception. - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, macKey, description); - prov.refresh(); - } - - private static EncryptionContext ctx(EncryptionMaterials mat) { - return new EncryptionContext.Builder() - .withMaterialDescription(mat.getMaterialDescription()).build(); - } + private static KeyPair encryptionPair; + private static SecretKey macKey; + private static KeyPair sigPair; + private Map description; + private EncryptionContext ctx; + + @BeforeClass + public static void setUpClass() throws Exception { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + sigPair = rsaGen.generateKeyPair(); + encryptionPair = rsaGen.generateKeyPair(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, Utils.getRng()); + macKey = macGen.generateKey(); + } + + @BeforeMethod + public void setUp() { + description = new HashMap(); + description.put("TestKey", "test value"); + description = Collections.unmodifiableMap(description); + ctx = new EncryptionContext.Builder().build(); + } + + @Test + public void constructWithMac() throws GeneralSecurityException { + AsymmetricStaticProvider prov = + new AsymmetricStaticProvider( + encryptionPair, macKey, Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(macKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void constructWithSigPair() throws GeneralSecurityException { + AsymmetricStaticProvider prov = + new AsymmetricStaticProvider( + encryptionPair, sigPair, Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void randomEnvelopeKeys() throws GeneralSecurityException { + AsymmetricStaticProvider prov = + new AsymmetricStaticProvider( + encryptionPair, macKey, Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(macKey, eMat.getSigningKey()); + + EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey2 = eMat2.getEncryptionKey(); + assertEquals(macKey, eMat.getSigningKey()); + + assertFalse("Envelope keys must be different", encryptionKey.equals(encryptionKey2)); + } + + @Test + public void testRefresh() { + // This does nothing, make sure we don't throw and exception. + AsymmetricStaticProvider prov = + new AsymmetricStaticProvider(encryptionPair, macKey, description); + prov.refresh(); + } + + private static EncryptionContext ctx(EncryptionMaterials mat) { + return new EncryptionContext.Builder() + .withMaterialDescription(mat.getMaterialDescription()) + .build(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProviderTests.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProviderTests.java index 048a72ca..0d3c87e6 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProviderTests.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/CachingMostRecentProviderTests.java @@ -2,6 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -12,11 +17,6 @@ import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -24,562 +24,582 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class CachingMostRecentProviderTests { - private static final String TABLE_NAME = "keystoreTable"; - private static final String MATERIAL_NAME = "material"; - private static final String MATERIAL_PARAM = "materialName"; - private static final SecretKey AES_KEY = new SecretKeySpec(new byte[]{0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, "AES"); - private static final SecretKey HMAC_KEY = new SecretKeySpec(new byte[]{0, - 1, 2, 3, 4, 5, 6, 7}, "HmacSHA256"); - private static final EncryptionMaterialsProvider BASE_PROVIDER = new SymmetricStaticProvider(AES_KEY, HMAC_KEY); - private static final DynamoDBEncryptor ENCRYPTOR = DynamoDBEncryptor.getInstance(BASE_PROVIDER); - - private AmazonDynamoDB client; - private Map methodCalls; - private ProviderStore store; - private EncryptionContext ctx; - - @BeforeMethod - public void setup() { - methodCalls = new HashMap(); - client = instrument(DynamoDBEmbedded.create(), AmazonDynamoDB.class, methodCalls); - MetaStore.createTable(client, TABLE_NAME, new ProvisionedThroughput(1L, 1L)); - store = new MetaStore(client, TABLE_NAME, ENCRYPTOR); - ctx = new EncryptionContext.Builder().build(); - methodCalls.clear(); - } - - @Test - public void testConstructors() { - final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 100, 1000); - assertEquals(MATERIAL_NAME, prov.getMaterialName()); - assertEquals(100, prov.getTtlInMills()); - assertEquals(-1, prov.getCurrentVersion()); - assertEquals(0, prov.getLastUpdated()); - - final CachingMostRecentProvider prov2 = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - assertEquals(MATERIAL_NAME, prov2.getMaterialName()); - assertEquals(500, prov2.getTtlInMills()); - assertEquals(-1, prov2.getCurrentVersion()); - assertEquals(0, prov2.getLastUpdated()); - } - - @Test - public void testSmallMaxCacheSize() { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 1); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Ensure the two materials are, in fact, different - assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); - - // Ensure the second set of materials are cached - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - - // Ensure the first set of materials are no longer cached, due to being the LRU - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); - - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); + private static final String TABLE_NAME = "keystoreTable"; + private static final String MATERIAL_NAME = "material"; + private static final String MATERIAL_PARAM = "materialName"; + private static final SecretKey AES_KEY = + new SecretKeySpec(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, "AES"); + private static final SecretKey HMAC_KEY = + new SecretKeySpec(new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, "HmacSHA256"); + private static final EncryptionMaterialsProvider BASE_PROVIDER = + new SymmetricStaticProvider(AES_KEY, HMAC_KEY); + private static final DynamoDBEncryptor ENCRYPTOR = DynamoDBEncryptor.getInstance(BASE_PROVIDER); + + private AmazonDynamoDB client; + private Map methodCalls; + private ProviderStore store; + private EncryptionContext ctx; + + @BeforeMethod + public void setup() { + methodCalls = new HashMap(); + client = instrument(DynamoDBEmbedded.create(), AmazonDynamoDB.class, methodCalls); + MetaStore.createTable(client, TABLE_NAME, new ProvisionedThroughput(1L, 1L)); + store = new MetaStore(client, TABLE_NAME, ENCRYPTOR); + ctx = new EncryptionContext.Builder().build(); + methodCalls.clear(); + } + + @Test + public void testConstructors() { + final CachingMostRecentProvider prov = + new CachingMostRecentProvider(store, MATERIAL_NAME, 100, 1000); + assertEquals(MATERIAL_NAME, prov.getMaterialName()); + assertEquals(100, prov.getTtlInMills()); + assertEquals(-1, prov.getCurrentVersion()); + assertEquals(0, prov.getLastUpdated()); + + final CachingMostRecentProvider prov2 = + new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + assertEquals(MATERIAL_NAME, prov2.getMaterialName()); + assertEquals(500, prov2.getTtlInMills()); + assertEquals(-1, prov2.getCurrentVersion()); + assertEquals(0, prov2.getLastUpdated()); + } + + @Test + public void testSmallMaxCacheSize() { + final Map attr1 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); + final EncryptionContext ctx1 = ctx(attr1); + final Map attr2 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); + final EncryptionContext ctx2 = ctx(attr2); + + final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 1); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Ensure the two materials are, in fact, different + assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); + + // Ensure the second set of materials are cached + final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + + // Ensure the first set of materials are no longer cached, due to being the LRU + final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); + assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); + + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); + } + + @Test + public void testSingleVersion() throws InterruptedException { + final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Ensure the cache is working + final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); + // Let the TTL be exceeded + Thread.sleep(500); + final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); + assertEquals(2, methodCalls.size()); + assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); // To get provider + assertEquals(0, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); + + assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); + assertEquals(eMat1.getSigningKey(), eMat3.getSigningKey()); + // Check algorithms. Right now we only support AES and HmacSHA256 + assertEquals("AES", eMat1.getEncryptionKey().getAlgorithm()); + assertEquals("HmacSHA256", eMat1.getSigningKey().getAlgorithm()); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = + new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); + methodCalls.clear(); + assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); + assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); + final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); + assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); + assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); + final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); + assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); + assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + } + + @Test + public void testSingleVersionWithRefresh() throws InterruptedException { + final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Ensure the cache is working + final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); + prov.refresh(); + final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); + assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); + assertEquals(0, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); + prov.refresh(); + + assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); + assertEquals(eMat1.getSigningKey(), eMat3.getSigningKey()); + + // Ensure that after cache refresh we only get one more hit as opposed to multiple + prov.getEncryptionMaterials(ctx); + Thread.sleep(700); + // Force refresh + prov.getEncryptionMaterials(ctx); + methodCalls.clear(); + // Check to ensure no more hits + assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); + assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); + assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); + assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); + assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = + new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); + methodCalls.clear(); + assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); + assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); + final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); + assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); + assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); + final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); + assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); + assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + } + + @Test + public void testTwoVersions() throws InterruptedException { + final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Create the new material + store.newProvider(MATERIAL_NAME); + methodCalls.clear(); + + // Ensure the cache is working + final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + // Let the TTL be exceeded + Thread.sleep(500); + final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); + + assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); // To retrieve current version + assertNull(methodCalls.get("putItem")); // No attempt to create a new item + assertEquals(1, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); + + assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); + assertFalse(eMat1.getSigningKey().equals(eMat3.getSigningKey())); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = + new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); + methodCalls.clear(); + assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); + assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); + final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); + assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); + assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); + final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); + assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); + assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); + // Get item will be hit once for the one old key + assertEquals(1, methodCalls.size()); + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); + } + + @Test + public void testTwoVersionsWithRefresh() throws InterruptedException { + final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 100); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Create the new material + store.newProvider(MATERIAL_NAME); + methodCalls.clear(); + // Ensure the cache is working + final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); + prov.refresh(); + final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); + assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); + assertEquals(1, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); + + assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); + assertFalse(eMat1.getSigningKey().equals(eMat3.getSigningKey())); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = + new CachingMostRecentProvider(store, MATERIAL_NAME, 500); + final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); + methodCalls.clear(); + assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); + assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); + final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); + assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); + assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); + final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); + assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); + assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); + // Get item will be hit once for the one old key + assertEquals(1, methodCalls.size()); + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); + } + + @Test + public void testSingleVersionTwoMaterials() throws InterruptedException { + final Map attr1 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); + final EncryptionContext ctx1 = ctx(attr1); + final Map attr2 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); + final EncryptionContext ctx2 = ctx(attr2); + + final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 100); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Ensure the two materials are, in fact, different + assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); + + // Ensure the cache is working + final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); + final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); + + // Let the TTL be exceeded + Thread.sleep(500); + final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); + assertEquals(2, methodCalls.size()); + assertEquals(1, (int) methodCalls.get("query")); // To find current version + assertEquals(1, (int) methodCalls.get("getItem")); // To get the provider + assertEquals(0, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); + methodCalls.clear(); + final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); + assertEquals(2, methodCalls.size()); + assertEquals(1, (int) methodCalls.get("query")); // To find current version + assertEquals(1, (int) methodCalls.get("getItem")); // To get the provider + assertEquals(0, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); + + assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); + assertEquals(eMat1_1.getSigningKey(), eMat3_1.getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), eMat3_2.getSigningKey()); + // Check algorithms. Right now we only support AES and HmacSHA256 + assertEquals("AES", eMat1_1.getEncryptionKey().getAlgorithm()); + assertEquals("AES", eMat1_2.getEncryptionKey().getAlgorithm()); + assertEquals("HmacSHA256", eMat1_1.getSigningKey().getAlgorithm()); + assertEquals("HmacSHA256", eMat1_2.getSigningKey().getAlgorithm()); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = new ExtendedProvider(store, 500, 100); + final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); + final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); + methodCalls.clear(); + assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); + assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); + assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); + assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); + final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); + final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); + assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); + assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); + assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); + assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); + final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); + final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); + assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); + assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); + assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); + assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + } + + @Test + public void testSingleVersionWithTwoMaterialsWithRefresh() throws InterruptedException { + final Map attr1 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); + final EncryptionContext ctx1 = ctx(attr1); + final Map attr2 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); + final EncryptionContext ctx2 = ctx(attr2); + + final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 100); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Ensure the two materials are, in fact, different + assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); + + // Ensure the cache is working + final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); + final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); + + prov.refresh(); + final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); + assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); + final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); + assertEquals(2, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(2, (int) methodCalls.getOrDefault("getItem", 0)); + assertEquals(0, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); + prov.refresh(); + + assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); + assertEquals(eMat1_1.getSigningKey(), eMat3_1.getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), eMat3_2.getSigningKey()); + + // Ensure that after cache refresh we only get one more hit as opposed to multiple + prov.getEncryptionMaterials(ctx1); + prov.getEncryptionMaterials(ctx2); + Thread.sleep(700); + // Force refresh + prov.getEncryptionMaterials(ctx1); + prov.getEncryptionMaterials(ctx2); + methodCalls.clear(); + // Check to ensure no more hits + assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); + assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); + assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); + assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); + assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); + + assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); + assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = new ExtendedProvider(store, 500, 100); + final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); + final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); + methodCalls.clear(); + assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); + assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); + assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); + assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); + final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); + final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); + assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); + assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); + assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); + assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); + final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); + final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); + assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); + assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); + assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); + assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + } + + @Test + public void testTwoVersionsWithTwoMaterialsWithRefresh() throws InterruptedException { + final Map attr1 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); + final EncryptionContext ctx1 = ctx(attr1); + final Map attr2 = + Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); + final EncryptionContext ctx2 = ctx(attr2); + + final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 100); + assertNull(methodCalls.get("putItem")); + final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); + // It's a new provider, so we see a single putItem + assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); + methodCalls.clear(); + // Create the new material + store.newProvider("material1"); + store.newProvider("material2"); + methodCalls.clear(); + // Ensure the cache is working + final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); + final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); + assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); + assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); + prov.refresh(); + final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); + final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); + assertEquals(2, (int) methodCalls.getOrDefault("query", 0)); // To find current version + assertEquals(2, (int) methodCalls.getOrDefault("getItem", 0)); + assertEquals(1, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); + assertEquals(1, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); + + assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); + assertFalse(eMat1_1.getSigningKey().equals(eMat3_1.getSigningKey())); + assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); + assertFalse(eMat1_2.getSigningKey().equals(eMat3_2.getSigningKey())); + + // Ensure we can decrypt all of them without hitting ddb more than the minimum + final CachingMostRecentProvider prov2 = new ExtendedProvider(store, 500, 100); + final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); + final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); + methodCalls.clear(); + assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); + assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); + assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); + assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); + final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); + final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); + assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); + assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); + assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); + assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); + final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); + final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); + assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); + assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); + assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); + assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); + // Get item will be hit twice, once for each old key + assertEquals(1, methodCalls.size()); + assertEquals(2, (int) methodCalls.getOrDefault("getItem", 0)); + } + + private static EncryptionContext ctx(final Map attr) { + return new EncryptionContext.Builder().withAttributeValues(attr).build(); + } + + private static EncryptionContext ctx( + final EncryptionMaterials mat, Map attr) { + return new EncryptionContext.Builder() + .withAttributeValues(attr) + .withMaterialDescription(mat.getMaterialDescription()) + .build(); + } + + private static EncryptionContext ctx(final EncryptionMaterials mat) { + return new EncryptionContext.Builder() + .withMaterialDescription(mat.getMaterialDescription()) + .build(); + } + + private static class ExtendedProvider extends CachingMostRecentProvider { + public ExtendedProvider(ProviderStore keystore, long ttlInMillis, int maxCacheSize) { + super(keystore, null, ttlInMillis, maxCacheSize); } - @Test - public void testSingleVersion() throws InterruptedException { - final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - // Let the TTL be exceeded - Thread.sleep(500); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - assertEquals(2, methodCalls.size()); - assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); // To get provider - assertEquals(0, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertEquals(eMat1.getSigningKey(), eMat3.getSigningKey()); - // Check algorithms. Right now we only support AES and HmacSHA256 - assertEquals("AES", eMat1.getEncryptionKey().getAlgorithm()); - assertEquals("HmacSHA256", eMat1.getSigningKey().getAlgorithm()); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - methodCalls.clear(); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + @Override + public long getCurrentVersion() { + throw new UnsupportedOperationException(); } - @Test - public void testSingleVersionWithRefresh() throws InterruptedException { - final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - prov.refresh(); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - prov.refresh(); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertEquals(eMat1.getSigningKey(), eMat3.getSigningKey()); - - // Ensure that after cache refresh we only get one more hit as opposed to multiple - prov.getEncryptionMaterials(ctx); - Thread.sleep(700); - // Force refresh - prov.getEncryptionMaterials(ctx); - methodCalls.clear(); - // Check to ensure no more hits - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - methodCalls.clear(); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); + @Override + protected String getMaterialName(final EncryptionContext context) { + return context.getAttributeValues().get(MATERIAL_PARAM).getS(); } - - @Test - public void testTwoVersions() throws InterruptedException { - final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Create the new material - store.newProvider(MATERIAL_NAME); - methodCalls.clear(); - - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - // Let the TTL be exceeded - Thread.sleep(500); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - - assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); // To retrieve current version - assertNull(methodCalls.get("putItem")); // No attempt to create a new item - assertEquals(1, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertFalse(eMat1.getSigningKey().equals(eMat3.getSigningKey())); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - methodCalls.clear(); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - // Get item will be hit once for the one old key - assertEquals(1, methodCalls.size()); - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); - } - - @Test - public void testTwoVersionsWithRefresh() throws InterruptedException { - final CachingMostRecentProvider prov = new CachingMostRecentProvider(store, MATERIAL_NAME, 100); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Create the new material - store.newProvider(MATERIAL_NAME); - methodCalls.clear(); - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - prov.refresh(); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); - assertEquals(1, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertFalse(eMat1.getSigningKey().equals(eMat3.getSigningKey())); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new CachingMostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - methodCalls.clear(); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - // Get item will be hit once for the one old key - assertEquals(1, methodCalls.size()); - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); - } - - @Test - public void testSingleVersionTwoMaterials() throws InterruptedException { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 100); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Ensure the two materials are, in fact, different - assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); - - // Ensure the cache is working - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); - - // Let the TTL be exceeded - Thread.sleep(500); - final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); - assertEquals(2, methodCalls.size()); - assertEquals(1, (int) methodCalls.get("query")); // To find current version - assertEquals(1, (int) methodCalls.get("getItem")); // To get the provider - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); - methodCalls.clear(); - final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); - assertEquals(2, methodCalls.size()); - assertEquals(1, (int) methodCalls.get("query")); // To find current version - assertEquals(1, (int) methodCalls.get("getItem")); // To get the provider - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); - - assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), eMat3_1.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat3_2.getSigningKey()); - // Check algorithms. Right now we only support AES and HmacSHA256 - assertEquals("AES", eMat1_1.getEncryptionKey().getAlgorithm()); - assertEquals("AES", eMat1_2.getEncryptionKey().getAlgorithm()); - assertEquals("HmacSHA256", eMat1_1.getSigningKey().getAlgorithm()); - assertEquals("HmacSHA256", eMat1_2.getSigningKey().getAlgorithm()); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new ExtendedProvider(store, 500, 100); - final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); - final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); - methodCalls.clear(); - assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); - assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); - assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); - assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); - final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); - final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); - assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); - assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); - assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); - assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); - final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); - final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); - assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); - assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); - assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); - assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - } - - @Test - public void testSingleVersionWithTwoMaterialsWithRefresh() throws InterruptedException { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 100); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Ensure the two materials are, in fact, different - assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); - - // Ensure the cache is working - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); - - prov.refresh(); - final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); - assertEquals(1, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(1, (int) methodCalls.getOrDefault("getItem", 0)); - final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); - assertEquals(2, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(2, (int) methodCalls.getOrDefault("getItem", 0)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); - prov.refresh(); - - assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), eMat3_1.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat3_2.getSigningKey()); - - // Ensure that after cache refresh we only get one more hit as opposed to multiple - prov.getEncryptionMaterials(ctx1); - prov.getEncryptionMaterials(ctx2); - Thread.sleep(700); - // Force refresh - prov.getEncryptionMaterials(ctx1); - prov.getEncryptionMaterials(ctx2); - methodCalls.clear(); - // Check to ensure no more hits - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new ExtendedProvider(store, 500, 100); - final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); - final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); - methodCalls.clear(); - assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); - assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); - assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); - assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); - final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); - final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); - assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); - assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); - assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); - assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); - final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); - final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); - assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); - assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); - assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); - assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - } - - @Test - public void testTwoVersionsWithTwoMaterialsWithRefresh() throws InterruptedException { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, new AttributeValue("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final CachingMostRecentProvider prov = new ExtendedProvider(store, 500, 100); - assertNull(methodCalls.get("putItem")); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - assertEquals(1, (int) methodCalls.getOrDefault("putItem", 0)); - methodCalls.clear(); - // Create the new material - store.newProvider("material1"); - store.newProvider("material2"); - methodCalls.clear(); - // Ensure the cache is working - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - assertTrue("Expected no calls but was " + methodCalls.toString(), methodCalls.isEmpty()); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); - prov.refresh(); - final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); - final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); - assertEquals(2, (int) methodCalls.getOrDefault("query", 0)); // To find current version - assertEquals(2, (int) methodCalls.getOrDefault("getItem", 0)); - assertEquals(1, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); - assertEquals(1, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); - - assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); - assertFalse(eMat1_1.getSigningKey().equals(eMat3_1.getSigningKey())); - assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); - assertFalse(eMat1_2.getSigningKey().equals(eMat3_2.getSigningKey())); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final CachingMostRecentProvider prov2 = new ExtendedProvider(store, 500, 100); - final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); - final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); - methodCalls.clear(); - assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); - assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); - assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); - assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); - final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); - final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); - assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); - assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); - assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); - assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); - final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); - final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); - assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); - assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); - assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); - assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); - // Get item will be hit twice, once for each old key - assertEquals(1, methodCalls.size()); - assertEquals(2, (int) methodCalls.getOrDefault("getItem", 0)); - } - - private static EncryptionContext ctx(final Map attr) { - return new EncryptionContext.Builder() - .withAttributeValues(attr).build(); - } - - private static EncryptionContext ctx(final EncryptionMaterials mat, Map attr) { - return new EncryptionContext.Builder() - .withAttributeValues(attr) - .withMaterialDescription(mat.getMaterialDescription()).build(); - } - - private static EncryptionContext ctx(final EncryptionMaterials mat) { - return new EncryptionContext.Builder() - .withMaterialDescription(mat.getMaterialDescription()).build(); - } - - private static class ExtendedProvider extends CachingMostRecentProvider { - public ExtendedProvider(ProviderStore keystore, long ttlInMillis, int maxCacheSize) { - super(keystore, null, ttlInMillis, maxCacheSize); - } - - @Override - public long getCurrentVersion() { - throw new UnsupportedOperationException(); - } - - @Override - protected String getMaterialName(final EncryptionContext context) { - return context.getAttributeValues().get(MATERIAL_PARAM).getS(); - } - } - - @SuppressWarnings("unchecked") - private static T instrument(final T obj, final Class clazz, final Map map) { - return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, + } + + @SuppressWarnings("unchecked") + private static T instrument( + final T obj, final Class clazz, final Map map) { + return (T) + Proxy.newProxyInstance( + clazz.getClassLoader(), + new Class[] {clazz}, new InvocationHandler() { - private final Object lock = new Object(); - - @Override - public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { - synchronized (lock) { - try { - final Integer oldCount = map.get(method.getName()); - if (oldCount != null) { - map.put(method.getName(), oldCount + 1); - } else { - map.put(method.getName(), 1); - } - return method.invoke(obj, args); - } catch (final InvocationTargetException ex) { - throw ex.getCause(); - } + private final Object lock = new Object(); + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) + throws Throwable { + synchronized (lock) { + try { + final Integer oldCount = map.get(method.getName()); + if (oldCount != null) { + map.put(method.getName(), oldCount + 1); + } else { + map.put(method.getName(), 1); } + return method.invoke(obj, args); + } catch (final InvocationTargetException ex) { + throw ex.getCause(); + } } - } - ); - } + } + }); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java index de4b60b5..76d4841a 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java @@ -12,6 +12,12 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.RequestClientOptions; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -26,10 +32,6 @@ import com.amazonaws.services.kms.model.GenerateDataKeyRequest; import com.amazonaws.services.kms.model.GenerateDataKeyResult; import com.amazonaws.util.Base64; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import javax.crypto.SecretKey; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; @@ -38,363 +40,418 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class DirectKmsMaterialProviderTest { - private FakeKMS kms; - private String keyId; - private Map description; - private EncryptionContext ctx; - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = new EncryptionContext.Builder().build(); - kms = new FakeKMS(); - keyId = kms.createKey().getKeyMetadata().getKeyId(); - } - - @Test - public void simple() throws GeneralSecurityException { - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - - String expectedEncAlg = encryptionKey.getAlgorithm() + "/" - + (encryptionKey.getEncoded().length * 8); - String expectedSigAlg = signingKey.getAlgorithm() + "/" - + (signingKey.getEncoded().length * 8); - - Map kmsCtx = kms.getSingleEc(); - assertEquals(expectedEncAlg, - kmsCtx.get("*" + WrappedRawMaterials.CONTENT_KEY_ALGORITHM + "*")); - assertEquals(expectedSigAlg, kmsCtx.get("*amzn-ddb-sig-alg*")); - } - - @Test - public void simpleWithKmsEc() throws GeneralSecurityException { - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", new AttributeValue("HashKeyValue")); - attrVals.put("rk", new AttributeValue("RangeKeyValue")); - - ctx = new EncryptionContext.Builder().withHashKeyName("hk").withRangeKeyName("rk") - .withTableName("KmsTableName").withAttributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals("HashKeyValue", kmsCtx.get("hk")); - assertEquals("RangeKeyValue", kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = new EncryptionContext.Builder(ctx(eMat)).withHashKeyName("hk") - .withRangeKeyName("rk").withTableName("KmsTableName").withAttributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test - public void simpleWithKmsEc2() throws GeneralSecurityException { - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", new AttributeValue().withN("10")); - attrVals.put("rk", new AttributeValue().withN("20")); - - ctx = new EncryptionContext.Builder().withHashKeyName("hk").withRangeKeyName("rk") - .withTableName("KmsTableName").withAttributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals("10", kmsCtx.get("hk")); - assertEquals("20", kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = new EncryptionContext.Builder(ctx(eMat)).withHashKeyName("hk") - .withRangeKeyName("rk").withTableName("KmsTableName").withAttributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test - public void simpleWithKmsEc3() throws GeneralSecurityException { - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", - new AttributeValue().withB(ByteBuffer.wrap("Foo".getBytes(StandardCharsets.UTF_8)))); - attrVals.put("rk", - new AttributeValue().withB(ByteBuffer.wrap("Bar".getBytes(StandardCharsets.UTF_8)))); - - ctx = new EncryptionContext.Builder().withHashKeyName("hk").withRangeKeyName("rk") - .withTableName("KmsTableName").withAttributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals(Base64.encodeAsString("Foo".getBytes(StandardCharsets.UTF_8)), - kmsCtx.get("hk")); - assertEquals(Base64.encodeAsString("Bar".getBytes(StandardCharsets.UTF_8)), - kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = new EncryptionContext.Builder(ctx(eMat)).withHashKeyName("hk") - .withRangeKeyName("rk").withTableName("KmsTableName").withAttributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test - public void randomEnvelopeKeys() throws GeneralSecurityException { - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey2 = eMat2.getEncryptionKey(); - - assertFalse("Envelope keys must be different", encryptionKey.equals(encryptionKey2)); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw and exception. - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); - prov.refresh(); - } - - @Test - public void explicitContentKeyAlgorithm() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); - - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", - eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - } - - @Test - public void explicitContentKeyLength128() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(16, encryptionKey.getEncoded().length); // 128 Bits - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES/128", - eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", eMat.getEncryptionKey().getAlgorithm()); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - } - - @Test - public void explicitContentKeyLength256() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(32, encryptionKey.getEncoded().length); // 256 Bits - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES/256", - eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", eMat.getEncryptionKey().getAlgorithm()); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - } - - @Test - public void extendedWithDerivedEncryptionKeyId() throws GeneralSecurityException { - ExtendedKmsMaterialProvider prov = new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); - String customKeyId = kms.createKey().getKeyMetadata().getKeyId(); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", new AttributeValue().withN("10")); - attrVals.put("rk", new AttributeValue().withN("20")); - attrVals.put("encryptionKeyId", new AttributeValue().withS(customKeyId)); - - ctx = new EncryptionContext.Builder().withHashKeyName("hk").withRangeKeyName("rk") - .withTableName("KmsTableName").withAttributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals("10", kmsCtx.get("hk")); - assertEquals("20", kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = new EncryptionContext.Builder(ctx(eMat)).withHashKeyName("hk") - .withRangeKeyName("rk").withTableName("KmsTableName").withAttributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void encryptionKeyIdMismatch() throws GeneralSecurityException { - DirectKmsMaterialProvider directProvider = new DirectKmsMaterialProvider(kms, keyId); - String customKeyId = kms.createKey().getKeyMetadata().getKeyId(); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", new AttributeValue().withN("10")); - attrVals.put("rk", new AttributeValue().withN("20")); - attrVals.put("encryptionKeyId", new AttributeValue().withS(customKeyId)); - - ctx = new EncryptionContext.Builder().withHashKeyName("hk").withRangeKeyName("rk") - .withTableName("KmsTableName").withAttributeValues(attrVals).build(); - EncryptionMaterials eMat = directProvider.getEncryptionMaterials(ctx); + private FakeKMS kms; + private String keyId; + private Map description; + private EncryptionContext ctx; + + @BeforeMethod + public void setUp() { + description = new HashMap<>(); + description.put("TestKey", "test value"); + description = Collections.unmodifiableMap(description); + ctx = new EncryptionContext.Builder().build(); + kms = new FakeKMS(); + keyId = kms.createKey().getKeyMetadata().getKeyId(); + } + + @Test + public void simple() throws GeneralSecurityException { + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + Key signingKey = eMat.getSigningKey(); + assertNotNull(signingKey); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(signingKey, dMat.getVerificationKey()); + + String expectedEncAlg = + encryptionKey.getAlgorithm() + "/" + (encryptionKey.getEncoded().length * 8); + String expectedSigAlg = signingKey.getAlgorithm() + "/" + (signingKey.getEncoded().length * 8); + + Map kmsCtx = kms.getSingleEc(); + assertEquals(expectedEncAlg, kmsCtx.get("*" + WrappedRawMaterials.CONTENT_KEY_ALGORITHM + "*")); + assertEquals(expectedSigAlg, kmsCtx.get("*amzn-ddb-sig-alg*")); + } + + @Test + public void simpleWithKmsEc() throws GeneralSecurityException { + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); + + Map attrVals = new HashMap<>(); + attrVals.put("hk", new AttributeValue("HashKeyValue")); + attrVals.put("rk", new AttributeValue("RangeKeyValue")); + + ctx = + new EncryptionContext.Builder() + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + Key signingKey = eMat.getSigningKey(); + assertNotNull(signingKey); + Map kmsCtx = kms.getSingleEc(); + assertEquals("HashKeyValue", kmsCtx.get("hk")); + assertEquals("RangeKeyValue", kmsCtx.get("rk")); + assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); + + EncryptionContext dCtx = + new EncryptionContext.Builder(ctx(eMat)) + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(signingKey, dMat.getVerificationKey()); + } + + @Test + public void simpleWithKmsEc2() throws GeneralSecurityException { + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); + + Map attrVals = new HashMap<>(); + attrVals.put("hk", new AttributeValue().withN("10")); + attrVals.put("rk", new AttributeValue().withN("20")); + + ctx = + new EncryptionContext.Builder() + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + Key signingKey = eMat.getSigningKey(); + assertNotNull(signingKey); + Map kmsCtx = kms.getSingleEc(); + assertEquals("10", kmsCtx.get("hk")); + assertEquals("20", kmsCtx.get("rk")); + assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); + + EncryptionContext dCtx = + new EncryptionContext.Builder(ctx(eMat)) + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(signingKey, dMat.getVerificationKey()); + } + + @Test + public void simpleWithKmsEc3() throws GeneralSecurityException { + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); + + Map attrVals = new HashMap<>(); + attrVals.put( + "hk", new AttributeValue().withB(ByteBuffer.wrap("Foo".getBytes(StandardCharsets.UTF_8)))); + attrVals.put( + "rk", new AttributeValue().withB(ByteBuffer.wrap("Bar".getBytes(StandardCharsets.UTF_8)))); + + ctx = + new EncryptionContext.Builder() + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + Key signingKey = eMat.getSigningKey(); + assertNotNull(signingKey); + assertNotNull(signingKey); + Map kmsCtx = kms.getSingleEc(); + assertEquals(Base64.encodeAsString("Foo".getBytes(StandardCharsets.UTF_8)), kmsCtx.get("hk")); + assertEquals(Base64.encodeAsString("Bar".getBytes(StandardCharsets.UTF_8)), kmsCtx.get("rk")); + assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); + + EncryptionContext dCtx = + new EncryptionContext.Builder(ctx(eMat)) + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(signingKey, dMat.getVerificationKey()); + } + + @Test + public void randomEnvelopeKeys() throws GeneralSecurityException { + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey2 = eMat2.getEncryptionKey(); + + assertFalse("Envelope keys must be different", encryptionKey.equals(encryptionKey2)); + } + + @Test + public void testRefresh() { + // This does nothing, make sure we don't throw and exception. + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId); + prov.refresh(); + } + + @Test + public void explicitContentKeyAlgorithm() throws GeneralSecurityException { + Map desc = new HashMap<>(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); + + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + } + + @Test + public void explicitContentKeyLength128() throws GeneralSecurityException { + Map desc = new HashMap<>(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(16, encryptionKey.getEncoded().length); // 128 Bits + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES/128", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals("AES", eMat.getEncryptionKey().getAlgorithm()); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + } + + @Test + public void explicitContentKeyLength256() throws GeneralSecurityException { + Map desc = new HashMap<>(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + + DirectKmsMaterialProvider prov = new DirectKmsMaterialProvider(kms, keyId, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(32, encryptionKey.getEncoded().length); // 256 Bits + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES/256", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals("AES", eMat.getEncryptionKey().getAlgorithm()); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + } + + @Test + public void extendedWithDerivedEncryptionKeyId() throws GeneralSecurityException { + ExtendedKmsMaterialProvider prov = + new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); + String customKeyId = kms.createKey().getKeyMetadata().getKeyId(); + + Map attrVals = new HashMap<>(); + attrVals.put("hk", new AttributeValue().withN("10")); + attrVals.put("rk", new AttributeValue().withN("20")); + attrVals.put("encryptionKeyId", new AttributeValue().withS(customKeyId)); + + ctx = + new EncryptionContext.Builder() + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + Key signingKey = eMat.getSigningKey(); + assertNotNull(signingKey); + Map kmsCtx = kms.getSingleEc(); + assertEquals("10", kmsCtx.get("hk")); + assertEquals("20", kmsCtx.get("rk")); + assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); + + EncryptionContext dCtx = + new EncryptionContext.Builder(ctx(eMat)) + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(signingKey, dMat.getVerificationKey()); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void encryptionKeyIdMismatch() throws GeneralSecurityException { + DirectKmsMaterialProvider directProvider = new DirectKmsMaterialProvider(kms, keyId); + String customKeyId = kms.createKey().getKeyMetadata().getKeyId(); + + Map attrVals = new HashMap<>(); + attrVals.put("hk", new AttributeValue().withN("10")); + attrVals.put("rk", new AttributeValue().withN("20")); + attrVals.put("encryptionKeyId", new AttributeValue().withS(customKeyId)); + + ctx = + new EncryptionContext.Builder() + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + EncryptionMaterials eMat = directProvider.getEncryptionMaterials(ctx); + + EncryptionContext dCtx = + new EncryptionContext.Builder(ctx(eMat)) + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + + ExtendedKmsMaterialProvider extendedProvider = + new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); + + extendedProvider.getDecryptionMaterials(dCtx); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void missingEncryptionKeyId() throws GeneralSecurityException { + ExtendedKmsMaterialProvider prov = + new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); + + Map attrVals = new HashMap<>(); + attrVals.put("hk", new AttributeValue().withN("10")); + attrVals.put("rk", new AttributeValue().withN("20")); + + ctx = + new EncryptionContext.Builder() + .withHashKeyName("hk") + .withRangeKeyName("rk") + .withTableName("KmsTableName") + .withAttributeValues(attrVals) + .build(); + prov.getEncryptionMaterials(ctx); + } + + @Test + public void generateDataKeyIsCalledWith256NumberOfBits() { + final AtomicBoolean gdkCalled = new AtomicBoolean(false); + AWSKMS kmsSpy = + new FakeKMS() { + @Override + public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) { + gdkCalled.set(true); + assertEquals((Integer) 32, r.getNumberOfBytes()); + assertNull(r.getKeySpec()); + return super.generateDataKey(r); + } + }; + assertFalse(gdkCalled.get()); + new DirectKmsMaterialProvider(kmsSpy, keyId).getEncryptionMaterials(ctx); + assertTrue(gdkCalled.get()); + } + + @Test + public void userAgentIsAdded() { + AWSKMS kmsSpy = + new FakeKMS() { + @Override + public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) { + assertTrue( + r.getRequestClientOptions() + .getClientMarker(RequestClientOptions.Marker.USER_AGENT) + .contains(DirectKmsMaterialProvider.USER_AGENT_PREFIX)); + return super.generateDataKey(r); + } + }; + new DirectKmsMaterialProvider(kmsSpy, keyId).getEncryptionMaterials(ctx); + } - EncryptionContext dCtx = new EncryptionContext.Builder(ctx(eMat)).withHashKeyName("hk") - .withRangeKeyName("rk").withTableName("KmsTableName").withAttributeValues(attrVals) - .build(); + private static class ExtendedKmsMaterialProvider extends DirectKmsMaterialProvider { + private final String encryptionKeyIdAttributeName; - ExtendedKmsMaterialProvider extendedProvider = new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); + public ExtendedKmsMaterialProvider( + AWSKMS kms, String encryptionKeyId, String encryptionKeyIdAttributeName) { + super(kms, encryptionKeyId); - extendedProvider.getDecryptionMaterials(dCtx); + this.encryptionKeyIdAttributeName = encryptionKeyIdAttributeName; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void missingEncryptionKeyId() throws GeneralSecurityException { - ExtendedKmsMaterialProvider prov = new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", new AttributeValue().withN("10")); - attrVals.put("rk", new AttributeValue().withN("20")); + @Override + protected String selectEncryptionKeyId(EncryptionContext context) + throws DynamoDBMappingException { + if (!context.getAttributeValues().containsKey(encryptionKeyIdAttributeName)) { + throw new DynamoDBMappingException("encryption key attribute is not provided"); + } - ctx = new EncryptionContext.Builder().withHashKeyName("hk").withRangeKeyName("rk") - .withTableName("KmsTableName").withAttributeValues(attrVals).build(); - prov.getEncryptionMaterials(ctx); + return context.getAttributeValues().get(encryptionKeyIdAttributeName).getS(); } - @Test - public void generateDataKeyIsCalledWith256NumberOfBits() { - final AtomicBoolean gdkCalled = new AtomicBoolean(false); - AWSKMS kmsSpy = new FakeKMS() { - @Override - public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) { - gdkCalled.set(true); - assertEquals((Integer) 32, r.getNumberOfBytes()); - assertNull(r.getKeySpec()); - return super.generateDataKey(r); - } - }; - assertFalse(gdkCalled.get()); - new DirectKmsMaterialProvider(kmsSpy, keyId).getEncryptionMaterials(ctx); - assertTrue(gdkCalled.get()); + @Override + protected void validateEncryptionKeyId(String encryptionKeyId, EncryptionContext context) + throws DynamoDBMappingException { + if (!context.getAttributeValues().containsKey(encryptionKeyIdAttributeName)) { + throw new DynamoDBMappingException("encryption key attribute is not provided"); + } + + String customEncryptionKeyId = + context.getAttributeValues().get(encryptionKeyIdAttributeName).getS(); + if (!customEncryptionKeyId.equals(encryptionKeyId)) { + throw new DynamoDBMappingException("encryption key ids do not match."); + } } - @Test - public void userAgentIsAdded() { - AWSKMS kmsSpy = new FakeKMS() { - @Override - public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) { - assertTrue(r.getRequestClientOptions().getClientMarker(RequestClientOptions.Marker.USER_AGENT) - .contains(DirectKmsMaterialProvider.USER_AGENT_PREFIX)); - return super.generateDataKey(r); - } - }; - new DirectKmsMaterialProvider(kmsSpy, keyId).getEncryptionMaterials(ctx); + @Override + protected DecryptResult decrypt(DecryptRequest request, EncryptionContext context) { + return super.decrypt(request, context); } - private static class ExtendedKmsMaterialProvider extends DirectKmsMaterialProvider { - private final String encryptionKeyIdAttributeName; - - public ExtendedKmsMaterialProvider(AWSKMS kms, String encryptionKeyId, String encryptionKeyIdAttributeName) { - super(kms, encryptionKeyId); - - this.encryptionKeyIdAttributeName = encryptionKeyIdAttributeName; - } - - @Override - protected String selectEncryptionKeyId(EncryptionContext context) throws DynamoDBMappingException { - if (!context.getAttributeValues().containsKey(encryptionKeyIdAttributeName)) { - throw new DynamoDBMappingException("encryption key attribute is not provided"); - } - - return context.getAttributeValues().get(encryptionKeyIdAttributeName).getS(); - } - - @Override - protected void validateEncryptionKeyId(String encryptionKeyId, EncryptionContext context) - throws DynamoDBMappingException { - if (!context.getAttributeValues().containsKey(encryptionKeyIdAttributeName)) { - throw new DynamoDBMappingException("encryption key attribute is not provided"); - } - - String customEncryptionKeyId = context.getAttributeValues().get(encryptionKeyIdAttributeName).getS(); - if (!customEncryptionKeyId.equals(encryptionKeyId)) { - throw new DynamoDBMappingException("encryption key ids do not match."); - } - } - - @Override - protected DecryptResult decrypt(DecryptRequest request, EncryptionContext context) { - return super.decrypt(request, context); - } - - @Override - protected GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest request, EncryptionContext context) { - return super.generateDataKey(request, context); - } + @Override + protected GenerateDataKeyResult generateDataKey( + GenerateDataKeyRequest request, EncryptionContext context) { + return super.generateDataKey(request, context); } + } - private static EncryptionContext ctx(EncryptionMaterials mat) { - return new EncryptionContext.Builder() - .withMaterialDescription(mat.getMaterialDescription()).build(); - } + private static EncryptionContext ctx(EncryptionMaterials mat) { + return new EncryptionContext.Builder() + .withMaterialDescription(mat.getMaterialDescription()) + .build(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProviderTest.java index 3cd570fa..8049abc3 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProviderTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/KeyStoreMaterialsProviderTest.java @@ -14,17 +14,16 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; import com.amazonaws.util.Base64; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.io.ByteArrayInputStream; import java.security.KeyFactory; import java.security.KeyStore; @@ -38,249 +37,287 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.fail; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class KeyStoreMaterialsProviderTest { - private static final String certPem = - "MIIDbTCCAlWgAwIBAgIJANdRvzVsW1CIMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV" + - "BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYDVQQKDANBV1MxGzAZBgNV" + - "BAMMEktleVN0b3JlIFRlc3QgQ2VydDAeFw0xMzA1MDgyMzMyMjBaFw0xMzA2MDcy" + - "MzMyMjBaME0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYD" + - "VQQKDANBV1MxGzAZBgNVBAMMEktleVN0b3JlIFRlc3QgQ2VydDCCASIwDQYJKoZI" + - "hvcNAQEBBQADggEPADCCAQoCggEBAJ8+umOX8x/Ma4OZishtYpcA676bwK5KScf3" + - "w+YGM37L12KTdnOyieiGtRW8p0fS0YvnhmVTvaky09I33bH+qy9gliuNL2QkyMxp" + - "uu1IwkTKKuB67CaKT6osYJLFxV/OwHcaZnTszzDgbAVg/Z+8IZxhPgxMzMa+7nDn" + - "hEm9Jd+EONq3PnRagnFeLNbMIePprdJzXHyNNiZKRRGQ/Mo9rr7mqMLSKnFNsmzB" + - "OIfeZM8nXeg+cvlmtXl72obwnGGw2ksJfaxTPm4eEhzRoAgkbjPPLHbwiJlc+GwF" + - "i8kh0Y3vQTj/gOFE4nzipkm7ux1lsGHNRVpVDWpjNd8Fl9JFELkCAwEAAaNQME4w" + - "HQYDVR0OBBYEFM0oGUuFAWlLXZaMXoJgGZxWqfOxMB8GA1UdIwQYMBaAFM0oGUuF" + - "AWlLXZaMXoJgGZxWqfOxMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB" + - "AAXCsXeC8ZRxovP0Wc6C5qv3d7dtgJJVzHwoIRt2YR3yScBa1XI40GKT80jP3MYH" + - "8xMu3mBQtcYrgRKZBy4GpHAyxoFTnPcuzq5Fg7dw7fx4E4OKIbWOahdxwtbVxQfZ" + - "UHnGG88Z0bq2twj7dALGyJhUDdiccckJGmJPOFMzjqsvoAu0n/p7eS6y5WZ5ewqw" + - "p7VwYOP3N9wVV7Podmkh1os+eCcp9GoFf0MHBMFXi2Ps2azKx8wHRIA5D1MZv/Va" + - "4L4/oTBKCjORpFlP7EhMksHBYnjqXLDP6awPMAgQNYB5J9zX6GfJsAgly3t4Rjr5" + - "cLuNYBmRuByFGo+SOdrj6D8="; - private static final String keyPem = - "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfPrpjl/MfzGuD" + - "mYrIbWKXAOu+m8CuSknH98PmBjN+y9dik3ZzsonohrUVvKdH0tGL54ZlU72pMtPS" + - "N92x/qsvYJYrjS9kJMjMabrtSMJEyirgeuwmik+qLGCSxcVfzsB3GmZ07M8w4GwF" + - "YP2fvCGcYT4MTMzGvu5w54RJvSXfhDjatz50WoJxXizWzCHj6a3Sc1x8jTYmSkUR" + - "kPzKPa6+5qjC0ipxTbJswTiH3mTPJ13oPnL5ZrV5e9qG8JxhsNpLCX2sUz5uHhIc" + - "0aAIJG4zzyx28IiZXPhsBYvJIdGN70E4/4DhROJ84qZJu7sdZbBhzUVaVQ1qYzXf" + - "BZfSRRC5AgMBAAECggEBAJMwx9eGe5LIwBfDtCPN93LbxwtHq7FtuQS8XrYexTpN" + - "76eN5c7LF+11lauh1HzuwAEw32iJHqVl9aQ5PxFm85O3ExbuSP+ngHJwx/bLacVr" + - "mHYlKGH3Net1WU5Qvz7vO7bbEBjDSj9DMJVIMSWUHv0MZO25jw2lLX/ufrgpvPf7" + - "KXSgXg/8uV7PbnTbBDNlg02u8eOc+IbH4O8XDKAhD+YQ8AE3pxtopJbb912U/cJs" + - "Y0hQ01zbkWYH7wL9BeQmR7+TEjjtr/IInNjnXmaOmSX867/rTSTuozaVrl1Ce7r8" + - "EmUDg9ZLZeKfoNYovMy08wnxWVX2J+WnNDjNiSOm+IECgYEA0v3jtGrOnKbd0d9E" + - "dbyIuhjgnwp+UsgALIiBeJYjhFS9NcWgs+02q/0ztqOK7g088KBBQOmiA+frLIVb" + - "uNCt/3jF6kJvHYkHMZ0eBEstxjVSM2UcxzJ6ceHZ68pmrru74382TewVosxccNy0" + - "glsUWNN0t5KQDcetaycRYg50MmcCgYEAwTb8klpNyQE8AWxVQlbOIEV24iarXxex" + - "7HynIg9lSeTzquZOXjp0m5omQ04psil2gZ08xjiudG+Dm7QKgYQcxQYUtZPQe15K" + - "m+2hQM0jA7tRfM1NAZHoTmUlYhzRNX6GWAqQXOgjOqBocT4ySBXRaSQq9zuZu36s" + - "fI17knap798CgYArDa2yOf0xEAfBdJqmn7MSrlLfgSenwrHuZGhu78wNi7EUUOBq" + - "9qOqUr+DrDmEO+VMgJbwJPxvaZqeehPuUX6/26gfFjFQSI7UO+hNHf4YLPc6D47g" + - "wtcjd9+c8q8jRqGfWWz+V4dOsf7G9PJMi0NKoNN3RgvpE+66J72vUZ26TwKBgEUq" + - "DdfGA7pEetp3kT2iHT9oHlpuRUJRFRv2s015/WQqVR+EOeF5Q2zADZpiTIK+XPGg" + - "+7Rpbem4UYBXPruGM1ZECv3E4AiJhGO0+Nhdln8reswWIc7CEEqf4nXwouNnW2gA" + - "wBTB9Hp0GW8QOKedR80/aTH/X9TCT7R2YRnY6JQ5AoGBAKjgPySgrNDhlJkW7jXR" + - "WiGpjGSAFPT9NMTvEHDo7oLTQ8AcYzcGQ7ISMRdVXR6GJOlFVsH4NLwuHGtcMTPK" + - "zoHbPHJyOn1SgC5tARD/1vm5CsG2hATRpWRQCTJFg5VRJ4R7Pz+HuxY4SoABcPQd" + - "K+MP8GlGqTldC6NaB1s7KuAX"; - - private static SecretKey encryptionKey; - private static SecretKey macKey; - private static KeyStore keyStore; - private static final String password = "Password"; - private static final PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray()); - - private Map description; - private EncryptionContext ctx; - private static PrivateKey privateKey; - private static Certificate certificate; - - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - - keyStore = KeyStore.getInstance("jceks"); - keyStore.load(null, password.toCharArray()); - - KeyFactory kf = KeyFactory.getInstance("RSA"); - PKCS8EncodedKeySpec rsaSpec = new PKCS8EncodedKeySpec(Base64.decode(keyPem)); - privateKey = kf.generatePrivate(rsaSpec); - CertificateFactory cf = CertificateFactory.getInstance("X509"); - certificate = cf.generateCertificate(new ByteArrayInputStream(Base64.decode(certPem))); - - - keyStore.setEntry("enc", new SecretKeyEntry(encryptionKey), passwordProtection); - keyStore.setEntry("sig", new SecretKeyEntry(macKey), passwordProtection); - keyStore.setEntry("enc-a", new PrivateKeyEntry(privateKey, new Certificate[]{certificate}), passwordProtection); - keyStore.setEntry("sig-a", new PrivateKeyEntry(privateKey, new Certificate[]{certificate}), passwordProtection); - keyStore.setCertificateEntry("trustedCert", certificate); + private static final String certPem = + "MIIDbTCCAlWgAwIBAgIJANdRvzVsW1CIMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV" + + "BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYDVQQKDANBV1MxGzAZBgNV" + + "BAMMEktleVN0b3JlIFRlc3QgQ2VydDAeFw0xMzA1MDgyMzMyMjBaFw0xMzA2MDcy" + + "MzMyMjBaME0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYD" + + "VQQKDANBV1MxGzAZBgNVBAMMEktleVN0b3JlIFRlc3QgQ2VydDCCASIwDQYJKoZI" + + "hvcNAQEBBQADggEPADCCAQoCggEBAJ8+umOX8x/Ma4OZishtYpcA676bwK5KScf3" + + "w+YGM37L12KTdnOyieiGtRW8p0fS0YvnhmVTvaky09I33bH+qy9gliuNL2QkyMxp" + + "uu1IwkTKKuB67CaKT6osYJLFxV/OwHcaZnTszzDgbAVg/Z+8IZxhPgxMzMa+7nDn" + + "hEm9Jd+EONq3PnRagnFeLNbMIePprdJzXHyNNiZKRRGQ/Mo9rr7mqMLSKnFNsmzB" + + "OIfeZM8nXeg+cvlmtXl72obwnGGw2ksJfaxTPm4eEhzRoAgkbjPPLHbwiJlc+GwF" + + "i8kh0Y3vQTj/gOFE4nzipkm7ux1lsGHNRVpVDWpjNd8Fl9JFELkCAwEAAaNQME4w" + + "HQYDVR0OBBYEFM0oGUuFAWlLXZaMXoJgGZxWqfOxMB8GA1UdIwQYMBaAFM0oGUuF" + + "AWlLXZaMXoJgGZxWqfOxMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB" + + "AAXCsXeC8ZRxovP0Wc6C5qv3d7dtgJJVzHwoIRt2YR3yScBa1XI40GKT80jP3MYH" + + "8xMu3mBQtcYrgRKZBy4GpHAyxoFTnPcuzq5Fg7dw7fx4E4OKIbWOahdxwtbVxQfZ" + + "UHnGG88Z0bq2twj7dALGyJhUDdiccckJGmJPOFMzjqsvoAu0n/p7eS6y5WZ5ewqw" + + "p7VwYOP3N9wVV7Podmkh1os+eCcp9GoFf0MHBMFXi2Ps2azKx8wHRIA5D1MZv/Va" + + "4L4/oTBKCjORpFlP7EhMksHBYnjqXLDP6awPMAgQNYB5J9zX6GfJsAgly3t4Rjr5" + + "cLuNYBmRuByFGo+SOdrj6D8="; + private static final String keyPem = + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfPrpjl/MfzGuD" + + "mYrIbWKXAOu+m8CuSknH98PmBjN+y9dik3ZzsonohrUVvKdH0tGL54ZlU72pMtPS" + + "N92x/qsvYJYrjS9kJMjMabrtSMJEyirgeuwmik+qLGCSxcVfzsB3GmZ07M8w4GwF" + + "YP2fvCGcYT4MTMzGvu5w54RJvSXfhDjatz50WoJxXizWzCHj6a3Sc1x8jTYmSkUR" + + "kPzKPa6+5qjC0ipxTbJswTiH3mTPJ13oPnL5ZrV5e9qG8JxhsNpLCX2sUz5uHhIc" + + "0aAIJG4zzyx28IiZXPhsBYvJIdGN70E4/4DhROJ84qZJu7sdZbBhzUVaVQ1qYzXf" + + "BZfSRRC5AgMBAAECggEBAJMwx9eGe5LIwBfDtCPN93LbxwtHq7FtuQS8XrYexTpN" + + "76eN5c7LF+11lauh1HzuwAEw32iJHqVl9aQ5PxFm85O3ExbuSP+ngHJwx/bLacVr" + + "mHYlKGH3Net1WU5Qvz7vO7bbEBjDSj9DMJVIMSWUHv0MZO25jw2lLX/ufrgpvPf7" + + "KXSgXg/8uV7PbnTbBDNlg02u8eOc+IbH4O8XDKAhD+YQ8AE3pxtopJbb912U/cJs" + + "Y0hQ01zbkWYH7wL9BeQmR7+TEjjtr/IInNjnXmaOmSX867/rTSTuozaVrl1Ce7r8" + + "EmUDg9ZLZeKfoNYovMy08wnxWVX2J+WnNDjNiSOm+IECgYEA0v3jtGrOnKbd0d9E" + + "dbyIuhjgnwp+UsgALIiBeJYjhFS9NcWgs+02q/0ztqOK7g088KBBQOmiA+frLIVb" + + "uNCt/3jF6kJvHYkHMZ0eBEstxjVSM2UcxzJ6ceHZ68pmrru74382TewVosxccNy0" + + "glsUWNN0t5KQDcetaycRYg50MmcCgYEAwTb8klpNyQE8AWxVQlbOIEV24iarXxex" + + "7HynIg9lSeTzquZOXjp0m5omQ04psil2gZ08xjiudG+Dm7QKgYQcxQYUtZPQe15K" + + "m+2hQM0jA7tRfM1NAZHoTmUlYhzRNX6GWAqQXOgjOqBocT4ySBXRaSQq9zuZu36s" + + "fI17knap798CgYArDa2yOf0xEAfBdJqmn7MSrlLfgSenwrHuZGhu78wNi7EUUOBq" + + "9qOqUr+DrDmEO+VMgJbwJPxvaZqeehPuUX6/26gfFjFQSI7UO+hNHf4YLPc6D47g" + + "wtcjd9+c8q8jRqGfWWz+V4dOsf7G9PJMi0NKoNN3RgvpE+66J72vUZ26TwKBgEUq" + + "DdfGA7pEetp3kT2iHT9oHlpuRUJRFRv2s015/WQqVR+EOeF5Q2zADZpiTIK+XPGg" + + "+7Rpbem4UYBXPruGM1ZECv3E4AiJhGO0+Nhdln8reswWIc7CEEqf4nXwouNnW2gA" + + "wBTB9Hp0GW8QOKedR80/aTH/X9TCT7R2YRnY6JQ5AoGBAKjgPySgrNDhlJkW7jXR" + + "WiGpjGSAFPT9NMTvEHDo7oLTQ8AcYzcGQ7ISMRdVXR6GJOlFVsH4NLwuHGtcMTPK" + + "zoHbPHJyOn1SgC5tARD/1vm5CsG2hATRpWRQCTJFg5VRJ4R7Pz+HuxY4SoABcPQd" + + "K+MP8GlGqTldC6NaB1s7KuAX"; + + private static SecretKey encryptionKey; + private static SecretKey macKey; + private static KeyStore keyStore; + private static final String password = "Password"; + private static final PasswordProtection passwordProtection = + new PasswordProtection(password.toCharArray()); + + private Map description; + private EncryptionContext ctx; + private static PrivateKey privateKey; + private static Certificate certificate; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, Utils.getRng()); + macKey = macGen.generateKey(); + + KeyGenerator aesGen = KeyGenerator.getInstance("AES"); + aesGen.init(128, Utils.getRng()); + encryptionKey = aesGen.generateKey(); + + keyStore = KeyStore.getInstance("jceks"); + keyStore.load(null, password.toCharArray()); + + KeyFactory kf = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec rsaSpec = new PKCS8EncodedKeySpec(Base64.decode(keyPem)); + privateKey = kf.generatePrivate(rsaSpec); + CertificateFactory cf = CertificateFactory.getInstance("X509"); + certificate = cf.generateCertificate(new ByteArrayInputStream(Base64.decode(certPem))); + + keyStore.setEntry("enc", new SecretKeyEntry(encryptionKey), passwordProtection); + keyStore.setEntry("sig", new SecretKeyEntry(macKey), passwordProtection); + keyStore.setEntry( + "enc-a", + new PrivateKeyEntry(privateKey, new Certificate[] {certificate}), + passwordProtection); + keyStore.setEntry( + "sig-a", + new PrivateKeyEntry(privateKey, new Certificate[] {certificate}), + passwordProtection); + keyStore.setCertificateEntry("trustedCert", certificate); + } + + @BeforeMethod + public void setUp() { + description = new HashMap(); + description.put("TestKey", "test value"); + description = Collections.unmodifiableMap(description); + ctx = new EncryptionContext.Builder().build(); + } + + @Test + @SuppressWarnings("unchecked") + public void simpleSymMac() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc", "sig", passwordProtection, passwordProtection, Collections.EMPTY_MAP); + EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); + assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); + assertEquals(macKey, encryptionMaterials.getSigningKey()); + + assertEquals( + encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); + assertEquals( + macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); + } + + @Test + @SuppressWarnings("unchecked") + public void simpleSymSig() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, + "enc", + "sig-a", + passwordProtection, + passwordProtection, + Collections.EMPTY_MAP); + EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); + assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); + assertEquals(privateKey, encryptionMaterials.getSigningKey()); + + assertEquals( + encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); + assertEquals( + certificate.getPublicKey(), + prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); + } + + @Test + public void equalSymDescMac() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc", "sig", passwordProtection, passwordProtection, description); + EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); + assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); + assertEquals(macKey, encryptionMaterials.getSigningKey()); + + assertEquals( + encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); + assertEquals( + macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); + } + + @Test + public void superSetSymDescMac() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc", "sig", passwordProtection, passwordProtection, description); + EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); + assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); + assertEquals(macKey, encryptionMaterials.getSigningKey()); + Map tmpDesc = + new HashMap(encryptionMaterials.getMaterialDescription()); + tmpDesc.put("randomValue", "random"); + + assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getDecryptionKey()); + assertEquals(macKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getVerificationKey()); + } + + @Test + @SuppressWarnings("unchecked") + public void subSetSymDescMac() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc", "sig", passwordProtection, passwordProtection, description); + EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); + assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); + assertEquals(macKey, encryptionMaterials.getSigningKey()); + + assertNull(prov.getDecryptionMaterials(ctx(Collections.EMPTY_MAP))); + } + + @Test + public void noMatchSymDescMac() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc", "sig", passwordProtection, passwordProtection, description); + EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); + assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); + assertEquals(macKey, encryptionMaterials.getSigningKey()); + Map tmpDesc = new HashMap(); + tmpDesc.put("randomValue", "random"); + + assertNull(prov.getDecryptionMaterials(ctx(tmpDesc))); + } + + @Test + public void testRefresh() throws Exception { + // Mostly make sure we don't throw an exception + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc", "sig", passwordProtection, passwordProtection, description); + prov.refresh(); + } + + @Test + public void asymSimpleMac() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc-a", "sig", passwordProtection, passwordProtection, description); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(macKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void asymSimpleSig() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc-a", "sig-a", passwordProtection, passwordProtection, description); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(privateKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); + } + + @Test + public void asymSigVerifyOnly() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "enc-a", "trustedCert", passwordProtection, null, description); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertNull(eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); + } + + @Test + public void asymSigEncryptOnly() throws Exception { + KeyStoreMaterialsProvider prov = + new KeyStoreMaterialsProvider( + keyStore, "trustedCert", "sig-a", null, passwordProtection, description); + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + assertEquals(privateKey, eMat.getSigningKey()); + + try { + prov.getDecryptionMaterials(ctx(eMat)); + fail("Expected exception"); + } catch (IllegalStateException ex) { + assertEquals("No private decryption key provided.", ex.getMessage()); } + } - @BeforeMethod - public void setUp() { - description = new HashMap(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = new EncryptionContext.Builder().build(); - } - - @Test - @SuppressWarnings("unchecked") - public void simpleSymMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, Collections.EMPTY_MAP); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); - } - - @Test - @SuppressWarnings("unchecked") - public void simpleSymSig() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig-a", passwordProtection, passwordProtection, Collections.EMPTY_MAP); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(privateKey, encryptionMaterials.getSigningKey()); + private static EncryptionContext ctx(EncryptionMaterials mat) { + return ctx(mat.getMaterialDescription()); + } - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); - assertEquals(certificate.getPublicKey(), prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); - } - - @Test - public void equalSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); - } - - @Test - public void superSetSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - Map tmpDesc = new HashMap(encryptionMaterials.getMaterialDescription()); - tmpDesc.put("randomValue", "random"); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getVerificationKey()); - } - - @Test - @SuppressWarnings("unchecked") - public void subSetSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - - assertNull(prov.getDecryptionMaterials(ctx(Collections.EMPTY_MAP))); - } - - - @Test - public void noMatchSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - Map tmpDesc = new HashMap(); - tmpDesc.put("randomValue", "random"); - - assertNull(prov.getDecryptionMaterials(ctx(tmpDesc))); - } - - @Test - public void testRefresh() throws Exception { - // Mostly make sure we don't throw an exception - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - prov.refresh(); - } - - @Test - public void asymSimpleMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void asymSimpleSig() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "sig-a", passwordProtection, passwordProtection, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(privateKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); - } - - @Test - public void asymSigVerifyOnly() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "trustedCert", passwordProtection, null, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertNull(eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); - } - - @Test - public void asymSigEncryptOnly() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "trustedCert", "sig-a", null, passwordProtection, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(privateKey, eMat.getSigningKey()); - - try { - prov.getDecryptionMaterials(ctx(eMat)); - fail("Expected exception"); - } catch (IllegalStateException ex) { - assertEquals("No private decryption key provided.", ex.getMessage()); - } - } - - private static EncryptionContext ctx(EncryptionMaterials mat) { - return ctx(mat.getMaterialDescription()); - } - - private static EncryptionContext ctx(Map desc) { - return new EncryptionContext.Builder() - .withMaterialDescription(desc).build(); - } + private static EncryptionContext ctx(Map desc) { + return new EncryptionContext.Builder().withMaterialDescription(desc).build(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProviderTest.java index 05e14ae0..03984ee3 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProviderTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/SymmetricStaticProviderTest.java @@ -14,149 +14,166 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class SymmetricStaticProviderTest { - private static SecretKey encryptionKey; - private static SecretKey macKey; - private static KeyPair sigPair; - private Map description; - private EncryptionContext ctx; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - sigPair = rsaGen.generateKeyPair(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = new EncryptionContext.Builder().build(); - } - - @Test - public void simpleMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider( - encryptionKey, macKey, Collections.emptyMap()); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - - assertEquals( - encryptionKey, - prov.getDecryptionMaterials(ctx(Collections.emptyMap())) - .getDecryptionKey()); - assertEquals( - macKey, - prov.getDecryptionMaterials(ctx(Collections.emptyMap())) - .getVerificationKey()); - } - - @Test - public void simpleSig() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, sigPair, Collections.emptyMap()); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(sigPair.getPrivate(), prov.getEncryptionMaterials(ctx).getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(Collections.emptyMap())).getDecryptionKey()); - assertEquals( - sigPair.getPublic(), - prov.getDecryptionMaterials(ctx(Collections.emptyMap())) - .getVerificationKey()); - } - - @Test - public void equalDescMac() { - - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(description)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(description)).getVerificationKey()); - - } - - @Test - public void supersetDescMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - Map superSet = new HashMap(description); - superSet.put("NewValue", "super!"); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(superSet)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(superSet)).getVerificationKey()); - } - - @Test - public void subsetDescMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - assertNull(prov.getDecryptionMaterials(ctx(Collections.emptyMap()))); - } - - @Test - public void noMatchDescMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - Map noMatch = new HashMap(); - noMatch.put("NewValue", "no match!"); - - assertNull(prov.getDecryptionMaterials(ctx(noMatch))); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw and exception. - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - prov.refresh(); - } - - @SuppressWarnings("unused") - private static EncryptionContext ctx(EncryptionMaterials mat) { - return ctx(mat.getMaterialDescription()); - } - - private static EncryptionContext ctx(Map desc) { - return new EncryptionContext.Builder() - .withMaterialDescription(desc).build(); - } + private static SecretKey encryptionKey; + private static SecretKey macKey; + private static KeyPair sigPair; + private Map description; + private EncryptionContext ctx; + + @BeforeClass + public static void setUpClass() throws Exception { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, Utils.getRng()); + sigPair = rsaGen.generateKeyPair(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, Utils.getRng()); + macKey = macGen.generateKey(); + + KeyGenerator aesGen = KeyGenerator.getInstance("AES"); + aesGen.init(128, Utils.getRng()); + encryptionKey = aesGen.generateKey(); + } + + @BeforeMethod + public void setUp() { + description = new HashMap(); + description.put("TestKey", "test value"); + description = Collections.unmodifiableMap(description); + ctx = new EncryptionContext.Builder().build(); + } + + @Test + public void simpleMac() { + SymmetricStaticProvider prov = + new SymmetricStaticProvider(encryptionKey, macKey, Collections.emptyMap()); + assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); + assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); + + assertEquals( + encryptionKey, + prov.getDecryptionMaterials(ctx(Collections.emptyMap())) + .getDecryptionKey()); + assertEquals( + macKey, + prov.getDecryptionMaterials(ctx(Collections.emptyMap())) + .getVerificationKey()); + } + + @Test + public void simpleSig() { + SymmetricStaticProvider prov = + new SymmetricStaticProvider(encryptionKey, sigPair, Collections.emptyMap()); + assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); + assertEquals(sigPair.getPrivate(), prov.getEncryptionMaterials(ctx).getSigningKey()); + + assertEquals( + encryptionKey, + prov.getDecryptionMaterials(ctx(Collections.emptyMap())) + .getDecryptionKey()); + assertEquals( + sigPair.getPublic(), + prov.getDecryptionMaterials(ctx(Collections.emptyMap())) + .getVerificationKey()); + } + + @Test + public void equalDescMac() { + + SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); + assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); + assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); + assertTrue( + prov.getEncryptionMaterials(ctx) + .getMaterialDescription() + .entrySet() + .containsAll(description.entrySet())); + + assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(description)).getDecryptionKey()); + assertEquals(macKey, prov.getDecryptionMaterials(ctx(description)).getVerificationKey()); + } + + @Test + public void supersetDescMac() { + SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); + assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); + assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); + assertTrue( + prov.getEncryptionMaterials(ctx) + .getMaterialDescription() + .entrySet() + .containsAll(description.entrySet())); + + Map superSet = new HashMap(description); + superSet.put("NewValue", "super!"); + + assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(superSet)).getDecryptionKey()); + assertEquals(macKey, prov.getDecryptionMaterials(ctx(superSet)).getVerificationKey()); + } + + @Test + public void subsetDescMac() { + SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); + assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); + assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); + assertTrue( + prov.getEncryptionMaterials(ctx) + .getMaterialDescription() + .entrySet() + .containsAll(description.entrySet())); + + assertNull(prov.getDecryptionMaterials(ctx(Collections.emptyMap()))); + } + + @Test + public void noMatchDescMac() { + SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); + assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); + assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); + assertTrue( + prov.getEncryptionMaterials(ctx) + .getMaterialDescription() + .entrySet() + .containsAll(description.entrySet())); + + Map noMatch = new HashMap(); + noMatch.put("NewValue", "no match!"); + + assertNull(prov.getDecryptionMaterials(ctx(noMatch))); + } + + @Test + public void testRefresh() { + // This does nothing, make sure we don't throw and exception. + SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); + prov.refresh(); + } + + @SuppressWarnings("unused") + private static EncryptionContext ctx(EncryptionMaterials mat) { + return ctx(mat.getMaterialDescription()); + } + + private static EncryptionContext ctx(Map desc) { + return new EncryptionContext.Builder().withMaterialDescription(desc).build(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProviderTest.java index 7589e394..57a1668c 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProviderTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/WrappedMaterialsProviderTest.java @@ -1,15 +1,13 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; + import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -18,347 +16,400 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class WrappedMaterialsProviderTest { - private static SecretKey symEncryptionKey; - private static SecretKey macKey; - private static KeyPair sigPair; - private static KeyPair encryptionPair; - private static SecureRandom rnd; - private Map description; - private EncryptionContext ctx; - - @BeforeClass - public static void setUpClass() throws NoSuchAlgorithmException { - rnd = new SecureRandom(); - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, rnd); - sigPair = rsaGen.generateKeyPair(); - encryptionPair = rsaGen.generateKeyPair(); - - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, rnd); - symEncryptionKey = aesGen.generateKey(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, rnd); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap(); - description.put("TestKey", "test value"); - ctx = new EncryptionContext.Builder().build(); - } - - @Test - public void simpleMac() throws GeneralSecurityException { - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void simpleSigPair() throws GeneralSecurityException { - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, sigPair, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void randomEnvelopeKeys() throws GeneralSecurityException { - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals(macKey, eMat.getSigningKey()); - - EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey2 = eMat2.getEncryptionKey(); - assertEquals(macKey, eMat.getSigningKey()); - - assertFalse("Envelope keys must be different", contentEncryptionKey.equals(contentEncryptionKey2)); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw an exception. - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, Collections.emptyMap()); - prov.refresh(); - } - - @Test - public void wrapUnwrapAsymMatExplicitWrappingAlgorithmPkcs1() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM, "RSA/ECB/PKCS1Padding"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("RSA/ECB/PKCS1Padding", eMat.getMaterialDescription().get(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapAsymMatExplicitWrappingAlgorithmPkcs2() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM, "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", eMat.getMaterialDescription().get(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapAsymMatExplicitContentKeyAlgorithm() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals("AES", contentEncryptionKey.getAlgorithm()); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapAsymMatExplicitContentKeyLength128() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals("AES", contentEncryptionKey.getAlgorithm()); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(16, contentEncryptionKey.getEncoded().length); // 128 Bits - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapAsymMatExplicitContentKeyLength256() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals("AES", contentEncryptionKey.getAlgorithm()); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(32, contentEncryptionKey.getEncoded().length); // 256 Bits - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void unwrapAsymMatExplicitEncAlgAes128() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); - - // Get materials we can test unwrapping on - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - - // Ensure "AES/128" on the created materials creates the expected key - Map aes128Desc = eMat.getMaterialDescription(); - aes128Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - EncryptionContext aes128Ctx = new EncryptionContext.Builder() - .withMaterialDescription(aes128Desc).build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(aes128Ctx); - assertEquals("AES/128", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); - assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void unwrapAsymMatExplicitEncAlgAes256() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); - - // Get materials we can test unwrapping on - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - - // Ensure "AES/256" on the created materials creates the expected key - Map aes256Desc = eMat.getMaterialDescription(); - aes256Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - EncryptionContext aes256Ctx = new EncryptionContext.Builder() - .withMaterialDescription(aes256Desc).build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(aes256Ctx); - assertEquals("AES/256", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); - assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapSymMatExplicitContentKeyAlgorithm() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals("AES", contentEncryptionKey.getAlgorithm()); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapSymMatExplicitContentKeyLength128() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals("AES", contentEncryptionKey.getAlgorithm()); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(16, contentEncryptionKey.getEncoded().length); // 128 Bits - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void wrapUnwrapSymMatExplicitContentKeyLength256() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey contentEncryptionKey = eMat.getEncryptionKey(); - assertNotNull(contentEncryptionKey); - assertEquals("AES", contentEncryptionKey.getAlgorithm()); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(32, contentEncryptionKey.getEncoded().length); // 256 Bits - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void unwrapSymMatExplicitEncAlgAes128() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); - - // Get materials we can test unwrapping on - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - - // Ensure "AES/128" on the created materials creates the expected key - Map aes128Desc = eMat.getMaterialDescription(); - aes128Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - EncryptionContext aes128Ctx = new EncryptionContext.Builder() - .withMaterialDescription(aes128Desc).build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(aes128Ctx); - assertEquals("AES/128", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); - assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void unwrapSymMatExplicitEncAlgAes256() throws GeneralSecurityException { - Map desc = new HashMap(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - WrappedMaterialsProvider prov = new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - - Map aes256Desc = eMat.getMaterialDescription(); - aes256Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - EncryptionContext aes256Ctx = new EncryptionContext.Builder() - .withMaterialDescription(aes256Desc).build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(aes256Ctx); - assertEquals("AES/256", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); - assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - private static EncryptionContext ctx(EncryptionMaterials mat) { - return new EncryptionContext.Builder() - .withMaterialDescription(mat.getMaterialDescription()).build(); - } + private static SecretKey symEncryptionKey; + private static SecretKey macKey; + private static KeyPair sigPair; + private static KeyPair encryptionPair; + private static SecureRandom rnd; + private Map description; + private EncryptionContext ctx; + + @BeforeClass + public static void setUpClass() throws NoSuchAlgorithmException { + rnd = new SecureRandom(); + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + rsaGen.initialize(2048, rnd); + sigPair = rsaGen.generateKeyPair(); + encryptionPair = rsaGen.generateKeyPair(); + + KeyGenerator aesGen = KeyGenerator.getInstance("AES"); + aesGen.init(128, rnd); + symEncryptionKey = aesGen.generateKey(); + + KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); + macGen.init(256, rnd); + macKey = macGen.generateKey(); + } + + @BeforeMethod + public void setUp() { + description = new HashMap(); + description.put("TestKey", "test value"); + ctx = new EncryptionContext.Builder().build(); + } + + @Test + public void simpleMac() throws GeneralSecurityException { + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + symEncryptionKey, symEncryptionKey, macKey, Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals(macKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void simpleSigPair() throws GeneralSecurityException { + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + symEncryptionKey, symEncryptionKey, sigPair, Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void randomEnvelopeKeys() throws GeneralSecurityException { + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + symEncryptionKey, symEncryptionKey, macKey, Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals(macKey, eMat.getSigningKey()); + + EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey2 = eMat2.getEncryptionKey(); + assertEquals(macKey, eMat.getSigningKey()); + + assertFalse( + "Envelope keys must be different", contentEncryptionKey.equals(contentEncryptionKey2)); + } + + @Test + public void testRefresh() { + // This does nothing, make sure we don't throw an exception. + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + symEncryptionKey, symEncryptionKey, macKey, Collections.emptyMap()); + prov.refresh(); + } + + @Test + public void wrapUnwrapAsymMatExplicitWrappingAlgorithmPkcs1() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM, "RSA/ECB/PKCS1Padding"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "RSA/ECB/PKCS1Padding", + eMat.getMaterialDescription().get(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapAsymMatExplicitWrappingAlgorithmPkcs2() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM, "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", + eMat.getMaterialDescription().get(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapAsymMatExplicitContentKeyAlgorithm() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), + encryptionPair.getPrivate(), + sigPair, + Collections.emptyMap()); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals("AES", contentEncryptionKey.getAlgorithm()); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapAsymMatExplicitContentKeyLength128() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals("AES", contentEncryptionKey.getAlgorithm()); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(16, contentEncryptionKey.getEncoded().length); // 128 Bits + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapAsymMatExplicitContentKeyLength256() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals("AES", contentEncryptionKey.getAlgorithm()); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(32, contentEncryptionKey.getEncoded().length); // 256 Bits + assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void unwrapAsymMatExplicitEncAlgAes128() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); + + // Get materials we can test unwrapping on + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + + // Ensure "AES/128" on the created materials creates the expected key + Map aes128Desc = eMat.getMaterialDescription(); + aes128Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + EncryptionContext aes128Ctx = + new EncryptionContext.Builder().withMaterialDescription(aes128Desc).build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(aes128Ctx); + assertEquals( + "AES/128", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); + assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void unwrapAsymMatExplicitEncAlgAes256() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider( + encryptionPair.getPublic(), encryptionPair.getPrivate(), sigPair, desc); + + // Get materials we can test unwrapping on + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + + // Ensure "AES/256" on the created materials creates the expected key + Map aes256Desc = eMat.getMaterialDescription(); + aes256Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + EncryptionContext aes256Ctx = + new EncryptionContext.Builder().withMaterialDescription(aes256Desc).build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(aes256Ctx); + assertEquals( + "AES/256", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); + assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); + assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapSymMatExplicitContentKeyAlgorithm() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals("AES", contentEncryptionKey.getAlgorithm()); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(macKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapSymMatExplicitContentKeyLength128() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals("AES", contentEncryptionKey.getAlgorithm()); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(16, contentEncryptionKey.getEncoded().length); // 128 Bits + assertEquals(macKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void wrapUnwrapSymMatExplicitContentKeyLength256() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + SecretKey contentEncryptionKey = eMat.getEncryptionKey(); + assertNotNull(contentEncryptionKey); + assertEquals("AES", contentEncryptionKey.getAlgorithm()); + assertEquals( + "AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(32, contentEncryptionKey.getEncoded().length); // 256 Bits + assertEquals(macKey, eMat.getSigningKey()); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals( + "AES", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals(contentEncryptionKey, dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void unwrapSymMatExplicitEncAlgAes128() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); + + // Get materials we can test unwrapping on + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + + // Ensure "AES/128" on the created materials creates the expected key + Map aes128Desc = eMat.getMaterialDescription(); + aes128Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); + EncryptionContext aes128Ctx = + new EncryptionContext.Builder().withMaterialDescription(aes128Desc).build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(aes128Ctx); + assertEquals( + "AES/128", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); + assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + @Test + public void unwrapSymMatExplicitEncAlgAes256() throws GeneralSecurityException { + Map desc = new HashMap(); + desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + + WrappedMaterialsProvider prov = + new WrappedMaterialsProvider(symEncryptionKey, symEncryptionKey, macKey, desc); + + EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + + Map aes256Desc = eMat.getMaterialDescription(); + aes256Desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); + EncryptionContext aes256Ctx = + new EncryptionContext.Builder().withMaterialDescription(aes256Desc).build(); + + DecryptionMaterials dMat = prov.getDecryptionMaterials(aes256Ctx); + assertEquals( + "AES/256", dMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); + assertEquals("AES", dMat.getDecryptionKey().getAlgorithm()); + assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); + assertEquals(macKey, dMat.getVerificationKey()); + } + + private static EncryptionContext ctx(EncryptionMaterials mat) { + return new EncryptionContext.Builder() + .withMaterialDescription(mat.getMaterialDescription()) + .build(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStoreTests.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStoreTests.java index ed6e5fde..2d3401cd 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStoreTests.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStoreTests.java @@ -12,6 +12,10 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.fail; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; @@ -23,11 +27,6 @@ import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -36,313 +35,325 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.fail; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; public class MetaStoreTests { - private static final String SOURCE_TABLE_NAME = "keystoreTable"; - private static final String DESTINATION_TABLE_NAME = "keystoreDestinationTable"; - private static final String MATERIAL_NAME = "material"; - private static final SecretKey AES_KEY = new SecretKeySpec(new byte[]{0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, "AES"); - private static final SecretKey TARGET_AES_KEY = new SecretKeySpec(new byte[]{0, - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, "AES"); - private static final SecretKey HMAC_KEY = new SecretKeySpec(new byte[]{0, - 1, 2, 3, 4, 5, 6, 7}, "HmacSHA256"); - private static final SecretKey TARGET_HMAC_KEY = new SecretKeySpec(new byte[]{0, - 2, 4, 6, 8, 10, 12, 14}, "HmacSHA256"); - private static final EncryptionMaterialsProvider BASE_PROVIDER = new SymmetricStaticProvider(AES_KEY, HMAC_KEY); - private static final EncryptionMaterialsProvider TARGET_BASE_PROVIDER = new SymmetricStaticProvider(TARGET_AES_KEY, TARGET_HMAC_KEY); - private static final DynamoDBEncryptor ENCRYPTOR = DynamoDBEncryptor.getInstance(BASE_PROVIDER); - private static final DynamoDBEncryptor TARGET_ENCRYPTOR = DynamoDBEncryptor.getInstance(TARGET_BASE_PROVIDER); - - private AmazonDynamoDB client; - private AmazonDynamoDB targetClient; - private MetaStore store; - private MetaStore targetStore; - private EncryptionContext ctx; - - private static class TestExtraDataSupplier implements MetaStore.ExtraDataSupplier { - - private final Map attributeValueMap; - private final Set signedOnlyFieldNames; - - public TestExtraDataSupplier(final Map attributeValueMap, - final Set signedOnlyFieldNames) { - this.attributeValueMap = attributeValueMap; - this.signedOnlyFieldNames = signedOnlyFieldNames; - } - - @Override - public Map getAttributes(String materialName, long version) { - return this.attributeValueMap; - } - - @Override - public Set getSignedOnlyFieldNames() { - return this.signedOnlyFieldNames; - } - } - - @BeforeMethod - public void setup() { - client = synchronize(DynamoDBEmbedded.create(), AmazonDynamoDB.class); - targetClient = synchronize(DynamoDBEmbedded.create(), AmazonDynamoDB.class); - MetaStore.createTable(client, SOURCE_TABLE_NAME, new ProvisionedThroughput(1L, 1L)); - //Creating Targeted DynamoDB Object - MetaStore.createTable(targetClient, DESTINATION_TABLE_NAME, new ProvisionedThroughput(1L, 1L)); - store = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR); - targetStore = new MetaStore(targetClient, DESTINATION_TABLE_NAME, TARGET_ENCRYPTOR); - ctx = new EncryptionContext.Builder().build(); - } - - @Test - public void testNoMaterials() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - } - - @Test - public void singleMaterial() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void singleMaterialExplicitAccess() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.getProvider(MATERIAL_NAME); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void singleMaterialExplicitAccessWithVersion() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.getProvider(MATERIAL_NAME, 0); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void singleMaterialWithImplicitCreation() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov = store.getProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void twoDifferentMaterials() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.newProvider(MATERIAL_NAME); - assertEquals(1, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - try { - prov2.getDecryptionMaterials(ctx(eMat)); - fail("Missing expected exception"); - } catch (final DynamoDBMappingException ex) { - // Expected Exception - } - final EncryptionMaterials eMat2 = prov2.getEncryptionMaterials(ctx); - assertEquals(1, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - } - - @Test - public void getOrCreateCollision() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.getOrCreate(MATERIAL_NAME, 0); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void getOrCreateWithContextSupplier() { - final Map attributeValueMap = new HashMap<>(); - attributeValueMap.put("CustomKeyId", new AttributeValue().withS("testCustomKeyId")); - attributeValueMap.put("KeyToken", new AttributeValue().withS("testKeyToken")); - - final Set signedOnlyAttributes = new HashSet<>(); - signedOnlyAttributes.add("CustomKeyId"); - - final TestExtraDataSupplier extraDataSupplier = new TestExtraDataSupplier( - attributeValueMap, signedOnlyAttributes); - - final MetaStore metaStore = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR, extraDataSupplier); - - assertEquals(-1, metaStore.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = metaStore.getOrCreate(MATERIAL_NAME, 0); - assertEquals(0, metaStore.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = metaStore.getOrCreate(MATERIAL_NAME, 0); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void replicateIntermediateKeysTest() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - - store.replicate(MATERIAL_NAME, 0, targetStore); - assertEquals(0, targetStore.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final DecryptionMaterials dMat = targetStore.getProvider(MATERIAL_NAME, 0).getDecryptionMaterials(ctx(eMat)); - - assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + private static final String SOURCE_TABLE_NAME = "keystoreTable"; + private static final String DESTINATION_TABLE_NAME = "keystoreDestinationTable"; + private static final String MATERIAL_NAME = "material"; + private static final SecretKey AES_KEY = + new SecretKeySpec(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, "AES"); + private static final SecretKey TARGET_AES_KEY = + new SecretKeySpec( + new byte[] {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, "AES"); + private static final SecretKey HMAC_KEY = + new SecretKeySpec(new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, "HmacSHA256"); + private static final SecretKey TARGET_HMAC_KEY = + new SecretKeySpec(new byte[] {0, 2, 4, 6, 8, 10, 12, 14}, "HmacSHA256"); + private static final EncryptionMaterialsProvider BASE_PROVIDER = + new SymmetricStaticProvider(AES_KEY, HMAC_KEY); + private static final EncryptionMaterialsProvider TARGET_BASE_PROVIDER = + new SymmetricStaticProvider(TARGET_AES_KEY, TARGET_HMAC_KEY); + private static final DynamoDBEncryptor ENCRYPTOR = DynamoDBEncryptor.getInstance(BASE_PROVIDER); + private static final DynamoDBEncryptor TARGET_ENCRYPTOR = + DynamoDBEncryptor.getInstance(TARGET_BASE_PROVIDER); + + private AmazonDynamoDB client; + private AmazonDynamoDB targetClient; + private MetaStore store; + private MetaStore targetStore; + private EncryptionContext ctx; + + private static class TestExtraDataSupplier implements MetaStore.ExtraDataSupplier { + + private final Map attributeValueMap; + private final Set signedOnlyFieldNames; + + public TestExtraDataSupplier( + final Map attributeValueMap, + final Set signedOnlyFieldNames) { + this.attributeValueMap = attributeValueMap; + this.signedOnlyFieldNames = signedOnlyFieldNames; } - @Test(expectedExceptions = IndexOutOfBoundsException.class) - public void replicateIntermediateKeysWhenMaterialNotFoundTest() { - store.replicate(MATERIAL_NAME, 0, targetStore); + @Override + public Map getAttributes(String materialName, long version) { + return this.attributeValueMap; } - @Test - public void newProviderCollision() throws InterruptedException { - final SlowNewProvider slowProv = new SlowNewProvider(); - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - assertEquals(-1, slowProv.slowStore.getMaxVersion(MATERIAL_NAME)); - - slowProv.start(); - Thread.sleep(100); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - slowProv.join(); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - assertEquals(0, slowProv.slowStore.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = slowProv.result; - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + @Override + public Set getSignedOnlyFieldNames() { + return this.signedOnlyFieldNames; } - - @Test(expectedExceptions = IndexOutOfBoundsException.class) - public void invalidVersion() { - store.getProvider(MATERIAL_NAME, 1000); + } + + @BeforeMethod + public void setup() { + client = synchronize(DynamoDBEmbedded.create(), AmazonDynamoDB.class); + targetClient = synchronize(DynamoDBEmbedded.create(), AmazonDynamoDB.class); + MetaStore.createTable(client, SOURCE_TABLE_NAME, new ProvisionedThroughput(1L, 1L)); + // Creating Targeted DynamoDB Object + MetaStore.createTable(targetClient, DESTINATION_TABLE_NAME, new ProvisionedThroughput(1L, 1L)); + store = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR); + targetStore = new MetaStore(targetClient, DESTINATION_TABLE_NAME, TARGET_ENCRYPTOR); + ctx = new EncryptionContext.Builder().build(); + } + + @Test + public void testNoMaterials() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + } + + @Test + public void singleMaterial() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov = store.newProvider(MATERIAL_NAME); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + + final EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test + public void singleMaterialExplicitAccess() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov2 = store.getProvider(MATERIAL_NAME); + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); + assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test + public void singleMaterialExplicitAccessWithVersion() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov2 = store.getProvider(MATERIAL_NAME, 0); + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); + assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test + public void singleMaterialWithImplicitCreation() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov = store.getProvider(MATERIAL_NAME); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + + final EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); + assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test + public void twoDifferentMaterials() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov2 = store.newProvider(MATERIAL_NAME); + assertEquals(1, store.getMaxVersion(MATERIAL_NAME)); + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + try { + prov2.getDecryptionMaterials(ctx(eMat)); + fail("Missing expected exception"); + } catch (final DynamoDBMappingException ex) { + // Expected Exception } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void invalidSignedOnlyField() { - final Map attributeValueMap = new HashMap<>(); - attributeValueMap.put("enc", new AttributeValue().withS("testEncryptionKey")); - - final Set signedOnlyAttributes = new HashSet<>(); - signedOnlyAttributes.add("enc"); - - final TestExtraDataSupplier extraDataSupplier = new TestExtraDataSupplier( - attributeValueMap, signedOnlyAttributes); - + final EncryptionMaterials eMat2 = prov2.getEncryptionMaterials(ctx); + assertEquals(1, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); + } + + @Test + public void getOrCreateCollision() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov2 = store.getOrCreate(MATERIAL_NAME, 0); + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test + public void getOrCreateWithContextSupplier() { + final Map attributeValueMap = new HashMap<>(); + attributeValueMap.put("CustomKeyId", new AttributeValue().withS("testCustomKeyId")); + attributeValueMap.put("KeyToken", new AttributeValue().withS("testKeyToken")); + + final Set signedOnlyAttributes = new HashSet<>(); + signedOnlyAttributes.add("CustomKeyId"); + + final TestExtraDataSupplier extraDataSupplier = + new TestExtraDataSupplier(attributeValueMap, signedOnlyAttributes); + + final MetaStore metaStore = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR, extraDataSupplier); - } - - private static EncryptionContext ctx(final EncryptionMaterials mat) { - return new EncryptionContext.Builder() - .withMaterialDescription(mat.getMaterialDescription()).build(); - } - private class SlowNewProvider extends Thread { - public volatile EncryptionMaterialsProvider result; - public ProviderStore slowStore = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR) { - @Override - public EncryptionMaterialsProvider newProvider(final String materialName) { - final long nextId = getMaxVersion(materialName) + 1; - try { - Thread.sleep(1000); - } catch (final InterruptedException e) { - // Ignored - } - return getOrCreate(materialName, nextId); + assertEquals(-1, metaStore.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov1 = metaStore.getOrCreate(MATERIAL_NAME, 0); + assertEquals(0, metaStore.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov2 = metaStore.getOrCreate(MATERIAL_NAME, 0); + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test + public void replicateIntermediateKeysTest() { + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + + final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + + store.replicate(MATERIAL_NAME, 0, targetStore); + assertEquals(0, targetStore.getMaxVersion(MATERIAL_NAME)); + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + final DecryptionMaterials dMat = + targetStore.getProvider(MATERIAL_NAME, 0).getDecryptionMaterials(ctx(eMat)); + + assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void replicateIntermediateKeysWhenMaterialNotFoundTest() { + store.replicate(MATERIAL_NAME, 0, targetStore); + } + + @Test + public void newProviderCollision() throws InterruptedException { + final SlowNewProvider slowProv = new SlowNewProvider(); + assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); + assertEquals(-1, slowProv.slowStore.getMaxVersion(MATERIAL_NAME)); + + slowProv.start(); + Thread.sleep(100); + final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); + slowProv.join(); + assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); + assertEquals(0, slowProv.slowStore.getMaxVersion(MATERIAL_NAME)); + final EncryptionMaterialsProvider prov2 = slowProv.result; + + final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); + final SecretKey encryptionKey = eMat.getEncryptionKey(); + assertNotNull(encryptionKey); + + final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); + assertEquals(encryptionKey, dMat.getDecryptionKey()); + assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void invalidVersion() { + store.getProvider(MATERIAL_NAME, 1000); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void invalidSignedOnlyField() { + final Map attributeValueMap = new HashMap<>(); + attributeValueMap.put("enc", new AttributeValue().withS("testEncryptionKey")); + + final Set signedOnlyAttributes = new HashSet<>(); + signedOnlyAttributes.add("enc"); + + final TestExtraDataSupplier extraDataSupplier = + new TestExtraDataSupplier(attributeValueMap, signedOnlyAttributes); + + new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR, extraDataSupplier); + } + + private static EncryptionContext ctx(final EncryptionMaterials mat) { + return new EncryptionContext.Builder() + .withMaterialDescription(mat.getMaterialDescription()) + .build(); + } + + private class SlowNewProvider extends Thread { + public volatile EncryptionMaterialsProvider result; + public ProviderStore slowStore = + new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR) { + @Override + public EncryptionMaterialsProvider newProvider(final String materialName) { + final long nextId = getMaxVersion(materialName) + 1; + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + // Ignored } + return getOrCreate(materialName, nextId); + } }; - @Override - public void run() { - result = slowStore.newProvider(MATERIAL_NAME); - } + @Override + public void run() { + result = slowStore.newProvider(MATERIAL_NAME); } - - @SuppressWarnings("unchecked") - private static T synchronize(final T obj, final Class clazz) { - return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, - new InvocationHandler() { - private final Object lock = new Object(); - - @Override - public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { - synchronized (lock) { - try { - return method.invoke(obj, args); - } catch (final InvocationTargetException ex) { - throw ex.getCause(); - } - } - } + } + + @SuppressWarnings("unchecked") + private static T synchronize(final T obj, final Class clazz) { + return (T) + Proxy.newProxyInstance( + clazz.getClassLoader(), + new Class[] {clazz}, + new InvocationHandler() { + private final Object lock = new Object(); + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) + throws Throwable { + synchronized (lock) { + try { + return method.invoke(obj, args); + } catch (final InvocationTargetException ex) { + throw ex.getCause(); + } } - ); - } + } + }); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperatorsTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperatorsTest.java index 5d917c8e..c90398f4 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperatorsTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperatorsTest.java @@ -1,150 +1,151 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; -import org.testng.annotations.Test; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableName; +import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap; +import static org.testng.AssertJUnit.assertEquals; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import java.util.HashMap; import java.util.Map; import java.util.function.Function; - -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableName; -import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap; -import static org.testng.AssertJUnit.assertEquals; +import org.testng.annotations.Test; public class EncryptionContextOperatorsTest { - @Test - public void testCreateEncryptionContextTableNameOverride_expectedOverride() { - Function myNewTableName = overrideEncryptionContextTableName("OriginalTableName", "MyNewTableName"); - - EncryptionContext context = new EncryptionContext.Builder().withTableName("OriginalTableName").build(); - - EncryptionContext newContext = myNewTableName.apply(context); - - assertEquals("OriginalTableName", context.getTableName()); - assertEquals("MyNewTableName", newContext.getTableName()); - } - - /** - * Some pretty clear repetition in null cases. May make sense to replace with data providers or parameterized - * classes for null cases - */ - @Test - public void testNullCasesCreateEncryptionContextTableNameOverride_nullOriginalTableName() { - assertEncryptionContextUnchanged(new EncryptionContext.Builder().withTableName("example").build(), - null, - "MyNewTableName"); - } - - @Test - public void testCreateEncryptionContextTableNameOverride_differentOriginalTableName() { - assertEncryptionContextUnchanged(new EncryptionContext.Builder().withTableName("example").build(), - "DifferentTableName", - "MyNewTableName"); - } - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverride_nullEncryptionContext() { - assertEncryptionContextUnchanged(null, - "DifferentTableName", - "MyNewTableName"); - } - - @Test - public void testCreateEncryptionContextTableNameOverrideMap_expectedOverride() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("OriginalTableName", "MyNewTableName"); - - - Function nameOverrideMap = - overrideEncryptionContextTableNameUsingMap(tableNameOverrides); - - EncryptionContext context = new EncryptionContext.Builder().withTableName("OriginalTableName").build(); - - EncryptionContext newContext = nameOverrideMap.apply(context); - - assertEquals("OriginalTableName", context.getTableName()); - assertEquals("MyNewTableName", newContext.getTableName()); - } - - @Test - public void testCreateEncryptionContextTableNameOverrideMap_multipleOverrides() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("OriginalTableName1", "MyNewTableName1"); - tableNameOverrides.put("OriginalTableName2", "MyNewTableName2"); - - - Function overrideOperator = - overrideEncryptionContextTableNameUsingMap(tableNameOverrides); - - EncryptionContext context = new EncryptionContext.Builder().withTableName("OriginalTableName1").build(); - - EncryptionContext newContext = overrideOperator.apply(context); - - assertEquals("OriginalTableName1", context.getTableName()); - assertEquals("MyNewTableName1", newContext.getTableName()); - - EncryptionContext context2 = new EncryptionContext.Builder().withTableName("OriginalTableName2").build(); - - EncryptionContext newContext2 = overrideOperator.apply(context2); - - assertEquals("OriginalTableName2", context2.getTableName()); - assertEquals("MyNewTableName2", newContext2.getTableName()); - - } - - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullEncryptionContextTableName() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("DifferentTableName", "MyNewTableName"); - assertEncryptionContextUnchangedFromMap(new EncryptionContext.Builder().build(), - tableNameOverrides); - } - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullEncryptionContext() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("DifferentTableName", "MyNewTableName"); - assertEncryptionContextUnchangedFromMap(null, - tableNameOverrides); - } - - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullOriginalTableName() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put(null, "MyNewTableName"); - assertEncryptionContextUnchangedFromMap(new EncryptionContext.Builder().withTableName("example").build(), - tableNameOverrides); - } - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullNewTableName() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("MyOriginalTableName", null); - assertEncryptionContextUnchangedFromMap(new EncryptionContext.Builder().withTableName("MyOriginalTableName").build(), - tableNameOverrides); - } - - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullMap() { - assertEncryptionContextUnchangedFromMap(new EncryptionContext.Builder().withTableName("MyOriginalTableName").build(), - null); - } - - - private void assertEncryptionContextUnchanged(EncryptionContext encryptionContext, String originalTableName, String newTableName) { - Function encryptionContextTableNameOverride = overrideEncryptionContextTableName(originalTableName, newTableName); - EncryptionContext newEncryptionContext = encryptionContextTableNameOverride.apply(encryptionContext); - assertEquals(encryptionContext, newEncryptionContext); - } - - - private void assertEncryptionContextUnchangedFromMap(EncryptionContext encryptionContext, Map overrideMap) { - Function encryptionContextTableNameOverrideFromMap = overrideEncryptionContextTableNameUsingMap(overrideMap); - EncryptionContext newEncryptionContext = encryptionContextTableNameOverrideFromMap.apply(encryptionContext); - assertEquals(encryptionContext, newEncryptionContext); - } + @Test + public void testCreateEncryptionContextTableNameOverride_expectedOverride() { + Function myNewTableName = + overrideEncryptionContextTableName("OriginalTableName", "MyNewTableName"); + + EncryptionContext context = + new EncryptionContext.Builder().withTableName("OriginalTableName").build(); + + EncryptionContext newContext = myNewTableName.apply(context); + + assertEquals("OriginalTableName", context.getTableName()); + assertEquals("MyNewTableName", newContext.getTableName()); + } + + /** + * Some pretty clear repetition in null cases. May make sense to replace with data providers or + * parameterized classes for null cases + */ + @Test + public void testNullCasesCreateEncryptionContextTableNameOverride_nullOriginalTableName() { + assertEncryptionContextUnchanged( + new EncryptionContext.Builder().withTableName("example").build(), null, "MyNewTableName"); + } + + @Test + public void testCreateEncryptionContextTableNameOverride_differentOriginalTableName() { + assertEncryptionContextUnchanged( + new EncryptionContext.Builder().withTableName("example").build(), + "DifferentTableName", + "MyNewTableName"); + } + + @Test + public void testNullCasesCreateEncryptionContextTableNameOverride_nullEncryptionContext() { + assertEncryptionContextUnchanged(null, "DifferentTableName", "MyNewTableName"); + } + + @Test + public void testCreateEncryptionContextTableNameOverrideMap_expectedOverride() { + Map tableNameOverrides = new HashMap<>(); + tableNameOverrides.put("OriginalTableName", "MyNewTableName"); + + Function nameOverrideMap = + overrideEncryptionContextTableNameUsingMap(tableNameOverrides); + + EncryptionContext context = + new EncryptionContext.Builder().withTableName("OriginalTableName").build(); + + EncryptionContext newContext = nameOverrideMap.apply(context); + + assertEquals("OriginalTableName", context.getTableName()); + assertEquals("MyNewTableName", newContext.getTableName()); + } + + @Test + public void testCreateEncryptionContextTableNameOverrideMap_multipleOverrides() { + Map tableNameOverrides = new HashMap<>(); + tableNameOverrides.put("OriginalTableName1", "MyNewTableName1"); + tableNameOverrides.put("OriginalTableName2", "MyNewTableName2"); + + Function overrideOperator = + overrideEncryptionContextTableNameUsingMap(tableNameOverrides); + + EncryptionContext context = + new EncryptionContext.Builder().withTableName("OriginalTableName1").build(); + + EncryptionContext newContext = overrideOperator.apply(context); + + assertEquals("OriginalTableName1", context.getTableName()); + assertEquals("MyNewTableName1", newContext.getTableName()); + + EncryptionContext context2 = + new EncryptionContext.Builder().withTableName("OriginalTableName2").build(); + + EncryptionContext newContext2 = overrideOperator.apply(context2); + + assertEquals("OriginalTableName2", context2.getTableName()); + assertEquals("MyNewTableName2", newContext2.getTableName()); + } + + @Test + public void + testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullEncryptionContextTableName() { + Map tableNameOverrides = new HashMap<>(); + tableNameOverrides.put("DifferentTableName", "MyNewTableName"); + assertEncryptionContextUnchangedFromMap( + new EncryptionContext.Builder().build(), tableNameOverrides); + } + + @Test + public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullEncryptionContext() { + Map tableNameOverrides = new HashMap<>(); + tableNameOverrides.put("DifferentTableName", "MyNewTableName"); + assertEncryptionContextUnchangedFromMap(null, tableNameOverrides); + } + + @Test + public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullOriginalTableName() { + Map tableNameOverrides = new HashMap<>(); + tableNameOverrides.put(null, "MyNewTableName"); + assertEncryptionContextUnchangedFromMap( + new EncryptionContext.Builder().withTableName("example").build(), tableNameOverrides); + } + + @Test + public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullNewTableName() { + Map tableNameOverrides = new HashMap<>(); + tableNameOverrides.put("MyOriginalTableName", null); + assertEncryptionContextUnchangedFromMap( + new EncryptionContext.Builder().withTableName("MyOriginalTableName").build(), + tableNameOverrides); + } + + @Test + public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullMap() { + assertEncryptionContextUnchangedFromMap( + new EncryptionContext.Builder().withTableName("MyOriginalTableName").build(), null); + } + + private void assertEncryptionContextUnchanged( + EncryptionContext encryptionContext, String originalTableName, String newTableName) { + Function encryptionContextTableNameOverride = + overrideEncryptionContextTableName(originalTableName, newTableName); + EncryptionContext newEncryptionContext = + encryptionContextTableNameOverride.apply(encryptionContext); + assertEquals(encryptionContext, newEncryptionContext); + } + + private void assertEncryptionContextUnchangedFromMap( + EncryptionContext encryptionContext, Map overrideMap) { + Function encryptionContextTableNameOverrideFromMap = + overrideEncryptionContextTableNameUsingMap(overrideMap); + EncryptionContext newEncryptionContext = + encryptionContextTableNameOverrideFromMap.apply(encryptionContext); + assertEquals(encryptionContext, newEncryptionContext); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java index 1dd6ad52..523a1958 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java @@ -14,12 +14,11 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; +import static com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller.marshall; +import static com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller.unmarshall; + import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.util.Base64; -import org.testng.Assert; -import org.testng.AssertJUnit; -import org.testng.annotations.Test; - import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collection; @@ -30,353 +29,387 @@ import java.util.List; import java.util.Map; import java.util.Set; - -import static com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller.marshall; -import static com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller.unmarshall; +import org.testng.Assert; +import org.testng.AssertJUnit; +import org.testng.annotations.Test; public class AttributeValueMarshallerTest { - @Test(expectedExceptions = IllegalArgumentException.class) - public void testEmpty() { - AttributeValue av = new AttributeValue(); - marshall(av); - } - - @Test - public void testNumber() { - AttributeValue av = new AttributeValue().withN("1337"); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testString() { - AttributeValue av = new AttributeValue().withS("1337"); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testByteBuffer() { - AttributeValue av = new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5})); - assertEquals(av, unmarshall(marshall(av))); - } - - // We can't use straight .equals for comparison because Attribute Values represents Sets - // as Lists and so incorrectly does an ordered comparison - - @Test - public void testNumberS() { - AttributeValue av = new AttributeValue().withNS(Collections.unmodifiableList(Arrays.asList("1337", "1", "5"))); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testNumberSOrdering() { - AttributeValue av1 = new AttributeValue().withNS(Collections.unmodifiableList(Arrays.asList("1337", "1", "5"))); - AttributeValue av2 = new AttributeValue().withNS(Collections.unmodifiableList(Arrays.asList("1", "5", "1337"))); - assertEquals(av1, av2); - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - Assert.assertEquals(buff1, buff2); - } - - @Test - public void testStringS() { - AttributeValue av = new AttributeValue().withSS(Collections.unmodifiableList(Arrays.asList("Bob", "Ann", "5"))); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testStringSOrdering() { - AttributeValue av1 = new AttributeValue().withSS(Collections.unmodifiableList(Arrays.asList("Bob", "Ann", "5"))); - AttributeValue av2 = new AttributeValue().withSS(Collections.unmodifiableList(Arrays.asList("Ann", "Bob", "5"))); - assertEquals(av1, av2); - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - Assert.assertEquals(buff1, buff2); - } - - @Test - public void testByteBufferS() { - AttributeValue av = new AttributeValue().withBS(Collections.unmodifiableList( - Arrays.asList(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}), - ByteBuffer.wrap(new byte[]{5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7})))); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testByteBufferSOrdering() { - AttributeValue av1 = new AttributeValue().withBS(Collections.unmodifiableList( - Arrays.asList(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}), - ByteBuffer.wrap(new byte[]{5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7})))); - AttributeValue av2 = new AttributeValue().withBS(Collections.unmodifiableList( - Arrays.asList(ByteBuffer.wrap(new byte[]{5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7}), - ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5}) - ))); - - assertEquals(av1, av2); - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - Assert.assertEquals(buff1, buff2); - } - - @Test - public void testBoolTrue() { - AttributeValue av = new AttributeValue().withBOOL(Boolean.TRUE); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testBoolFalse() { - AttributeValue av = new AttributeValue().withBOOL(Boolean.FALSE); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testNULL() { - AttributeValue av = new AttributeValue().withNULL(Boolean.TRUE); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test(expectedExceptions = NullPointerException.class) - public void testActualNULL() { - unmarshall(marshall(null)); - } - - @Test - public void testEmptyList() { - AttributeValue av = new AttributeValue().withL(); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testListOfString() { - AttributeValue av = new AttributeValue().withL(new AttributeValue().withS("StringValue")); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testList() { - AttributeValue av = new AttributeValue().withL( + @Test(expectedExceptions = IllegalArgumentException.class) + public void testEmpty() { + AttributeValue av = new AttributeValue(); + marshall(av); + } + + @Test + public void testNumber() { + AttributeValue av = new AttributeValue().withN("1337"); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testString() { + AttributeValue av = new AttributeValue().withS("1337"); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testByteBuffer() { + AttributeValue av = new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5})); + assertEquals(av, unmarshall(marshall(av))); + } + + // We can't use straight .equals for comparison because Attribute Values represents Sets + // as Lists and so incorrectly does an ordered comparison + + @Test + public void testNumberS() { + AttributeValue av = + new AttributeValue().withNS(Collections.unmodifiableList(Arrays.asList("1337", "1", "5"))); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testNumberSOrdering() { + AttributeValue av1 = + new AttributeValue().withNS(Collections.unmodifiableList(Arrays.asList("1337", "1", "5"))); + AttributeValue av2 = + new AttributeValue().withNS(Collections.unmodifiableList(Arrays.asList("1", "5", "1337"))); + assertEquals(av1, av2); + ByteBuffer buff1 = marshall(av1); + ByteBuffer buff2 = marshall(av2); + Assert.assertEquals(buff1, buff2); + } + + @Test + public void testStringS() { + AttributeValue av = + new AttributeValue().withSS(Collections.unmodifiableList(Arrays.asList("Bob", "Ann", "5"))); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testStringSOrdering() { + AttributeValue av1 = + new AttributeValue().withSS(Collections.unmodifiableList(Arrays.asList("Bob", "Ann", "5"))); + AttributeValue av2 = + new AttributeValue().withSS(Collections.unmodifiableList(Arrays.asList("Ann", "Bob", "5"))); + assertEquals(av1, av2); + ByteBuffer buff1 = marshall(av1); + ByteBuffer buff2 = marshall(av2); + Assert.assertEquals(buff1, buff2); + } + + @Test + public void testByteBufferS() { + AttributeValue av = + new AttributeValue() + .withBS( + Collections.unmodifiableList( + Arrays.asList( + ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5}), + ByteBuffer.wrap(new byte[] {5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7})))); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testByteBufferSOrdering() { + AttributeValue av1 = + new AttributeValue() + .withBS( + Collections.unmodifiableList( + Arrays.asList( + ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5}), + ByteBuffer.wrap(new byte[] {5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7})))); + AttributeValue av2 = + new AttributeValue() + .withBS( + Collections.unmodifiableList( + Arrays.asList( + ByteBuffer.wrap(new byte[] {5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7}), + ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5})))); + + assertEquals(av1, av2); + ByteBuffer buff1 = marshall(av1); + ByteBuffer buff2 = marshall(av2); + Assert.assertEquals(buff1, buff2); + } + + @Test + public void testBoolTrue() { + AttributeValue av = new AttributeValue().withBOOL(Boolean.TRUE); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testBoolFalse() { + AttributeValue av = new AttributeValue().withBOOL(Boolean.FALSE); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testNULL() { + AttributeValue av = new AttributeValue().withNULL(Boolean.TRUE); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testActualNULL() { + unmarshall(marshall(null)); + } + + @Test + public void testEmptyList() { + AttributeValue av = new AttributeValue().withL(); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testListOfString() { + AttributeValue av = new AttributeValue().withL(new AttributeValue().withS("StringValue")); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testList() { + AttributeValue av = + new AttributeValue() + .withL( new AttributeValue().withS("StringValue"), new AttributeValue().withN("1000"), new AttributeValue().withBOOL(Boolean.TRUE)); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testListWithNull() { - final AttributeValue av = new AttributeValue().withL( + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testListWithNull() { + final AttributeValue av = + new AttributeValue() + .withL( new AttributeValue().withS("StringValue"), new AttributeValue().withN("1000"), new AttributeValue().withBOOL(Boolean.TRUE), null); - try { - marshall(av); - Assert.fail("Unexpected success"); - } catch (final NullPointerException npe) { - Assert.assertEquals("Encountered null list entry value while marshalling attribute value {L: [{S: StringValue,}, {N: 1000,}, {BOOL: true}, null],}", - npe.getMessage()); - } + try { + marshall(av); + Assert.fail("Unexpected success"); + } catch (final NullPointerException npe) { + Assert.assertEquals( + "Encountered null list entry value while marshalling attribute value {L: [{S: StringValue,}, {N: 1000,}, {BOOL: true}, null],}", + npe.getMessage()); } + } - @Test - public void testListDuplicates() { - AttributeValue av = new AttributeValue().withL( + @Test + public void testListDuplicates() { + AttributeValue av = + new AttributeValue() + .withL( new AttributeValue().withN("1000"), new AttributeValue().withN("1000"), new AttributeValue().withN("1000"), new AttributeValue().withN("1000")); - AttributeValue result = unmarshall(marshall(av)); - assertEquals(av, result); - Assert.assertEquals(4, result.getL().size()); - } - - @Test - public void testComplexList() { - final List list1 = Arrays.asList( - new AttributeValue().withS("StringValue"), - new AttributeValue().withN("1000"), - new AttributeValue().withBOOL(Boolean.TRUE)); - final List list22 = Arrays.asList( - new AttributeValue().withS("AWS"), - new AttributeValue().withN("-3700"), - new AttributeValue().withBOOL(Boolean.FALSE)); - final List list2 = Arrays.asList( - new AttributeValue().withL(list22), - new AttributeValue().withNULL(Boolean.TRUE)); - AttributeValue av = new AttributeValue().withL( + AttributeValue result = unmarshall(marshall(av)); + assertEquals(av, result); + Assert.assertEquals(4, result.getL().size()); + } + + @Test + public void testComplexList() { + final List list1 = + Arrays.asList( + new AttributeValue().withS("StringValue"), + new AttributeValue().withN("1000"), + new AttributeValue().withBOOL(Boolean.TRUE)); + final List list22 = + Arrays.asList( + new AttributeValue().withS("AWS"), + new AttributeValue().withN("-3700"), + new AttributeValue().withBOOL(Boolean.FALSE)); + final List list2 = + Arrays.asList( + new AttributeValue().withL(list22), new AttributeValue().withNULL(Boolean.TRUE)); + AttributeValue av = + new AttributeValue() + .withL( new AttributeValue().withS("StringValue1"), new AttributeValue().withL(list1), new AttributeValue().withN("50"), new AttributeValue().withL(list2)); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testEmptyMap() { - Map map = new HashMap(); - AttributeValue av = new AttributeValue().withM(map); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testSimpleMap() { - Map map = new HashMap(); - map.put("KeyValue", new AttributeValue().withS("ValueValue")); - AttributeValue av = new AttributeValue().withM(map); - assertEquals(av, unmarshall(marshall(av))); - } - - @Test - public void testSimpleMapWithNull() { - final Map map = new HashMap(); - map.put("KeyValue", new AttributeValue().withS("ValueValue")); - map.put("NullKeyValue", null); - - final AttributeValue av = new AttributeValue().withM(map); - - try { - marshall(av); - Assert.fail("Unexpected success"); - } catch (final NullPointerException npe) { - Assert.assertEquals("Encountered null map value for key NullKeyValue while marshalling attribute value {M: {KeyValue={S: ValueValue,}, NullKeyValue=null},}", - npe.getMessage()); - } - } - - @Test - public void testMapOrdering() { - LinkedHashMap m1 = new LinkedHashMap(); - LinkedHashMap m2 = new LinkedHashMap(); - - m1.put("Value1", new AttributeValue().withN("1")); - m1.put("Value2", new AttributeValue().withBOOL(Boolean.TRUE)); - - m2.put("Value2", new AttributeValue().withBOOL(Boolean.TRUE)); - m2.put("Value1", new AttributeValue().withN("1")); - - AttributeValue av1 = new AttributeValue().withM(m1); - AttributeValue av2 = new AttributeValue().withM(m2); - - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - Assert.assertEquals(buff1, buff2); - assertEquals(av1, unmarshall(buff1)); - assertEquals(av1, unmarshall(buff2)); - assertEquals(av2, unmarshall(buff1)); - assertEquals(av2, unmarshall(buff2)); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testEmptyMap() { + Map map = new HashMap(); + AttributeValue av = new AttributeValue().withM(map); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testSimpleMap() { + Map map = new HashMap(); + map.put("KeyValue", new AttributeValue().withS("ValueValue")); + AttributeValue av = new AttributeValue().withM(map); + assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testSimpleMapWithNull() { + final Map map = new HashMap(); + map.put("KeyValue", new AttributeValue().withS("ValueValue")); + map.put("NullKeyValue", null); + + final AttributeValue av = new AttributeValue().withM(map); + + try { + marshall(av); + Assert.fail("Unexpected success"); + } catch (final NullPointerException npe) { + Assert.assertEquals( + "Encountered null map value for key NullKeyValue while marshalling attribute value {M: {KeyValue={S: ValueValue,}, NullKeyValue=null},}", + npe.getMessage()); } - - @Test - public void testComplexMap() { - AttributeValue av = buildComplexAttributeValue(); - assertEquals(av, unmarshall(marshall(av))); + } + + @Test + public void testMapOrdering() { + LinkedHashMap m1 = new LinkedHashMap(); + LinkedHashMap m2 = new LinkedHashMap(); + + m1.put("Value1", new AttributeValue().withN("1")); + m1.put("Value2", new AttributeValue().withBOOL(Boolean.TRUE)); + + m2.put("Value2", new AttributeValue().withBOOL(Boolean.TRUE)); + m2.put("Value1", new AttributeValue().withN("1")); + + AttributeValue av1 = new AttributeValue().withM(m1); + AttributeValue av2 = new AttributeValue().withM(m2); + + ByteBuffer buff1 = marshall(av1); + ByteBuffer buff2 = marshall(av2); + Assert.assertEquals(buff1, buff2); + assertEquals(av1, unmarshall(buff1)); + assertEquals(av1, unmarshall(buff2)); + assertEquals(av2, unmarshall(buff1)); + assertEquals(av2, unmarshall(buff2)); + } + + @Test + public void testComplexMap() { + AttributeValue av = buildComplexAttributeValue(); + assertEquals(av, unmarshall(marshall(av))); + } + + // This test ensures that an AttributeValue marshalled by an older + // version of this library still unmarshalls correctly. It also + // ensures that old and new marshalling is identical. + @Test + public void testVersioningCompatibility() { + AttributeValue newObject = buildComplexAttributeValue(); + byte[] oldBytes = Base64.decode(COMPLEX_ATTRIBUTE_MARSHALLED); + byte[] newBytes = marshall(newObject).array(); + AssertJUnit.assertArrayEquals(oldBytes, newBytes); + + AttributeValue oldObject = unmarshall(ByteBuffer.wrap(oldBytes)); + assertEquals(oldObject, newObject); + } + + private static final String COMPLEX_ATTRIBUTE_MARSHALLED = + "AE0AAAADAHM" + + "AAAAJSW5uZXJMaXN0AEwAAAAGAHMAAAALQ29tcGxleExpc3QAbgAAAAE1AGIAA" + + "AAGAAECAwQFAEwAAAAFAD8BAAAAAABMAAAAAQA/AABNAAAAAwBzAAAABFBpbms" + + "AcwAAAAVGbG95ZABzAAAABFRlc3QAPwEAcwAAAAdWZXJzaW9uAG4AAAABMQAAA" + + "E0AAAADAHMAAAAETGlzdABMAAAABQBuAAAAATUAbgAAAAE0AG4AAAABMwBuAAA" + + "AATIAbgAAAAExAHMAAAADTWFwAE0AAAABAHMAAAAGTmVzdGVkAD8BAHMAAAAEV" + + "HJ1ZQA/AQBzAAAACVNpbmdsZU1hcABNAAAAAQBzAAAAA0ZPTwBzAAAAA0JBUgB" + + "zAAAACVN0cmluZ1NldABTAAAAAwAAAANiYXIAAAADYmF6AAAAA2Zvbw=="; + + private static AttributeValue buildComplexAttributeValue() { + Map floydMap = new HashMap(); + floydMap.put("Pink", new AttributeValue().withS("Floyd")); + floydMap.put("Version", new AttributeValue().withN("1")); + floydMap.put("Test", new AttributeValue().withBOOL(Boolean.TRUE)); + List floydList = + Arrays.asList( + new AttributeValue().withBOOL(Boolean.TRUE), + new AttributeValue().withNULL(Boolean.TRUE), + new AttributeValue().withNULL(Boolean.TRUE), + new AttributeValue().withL(new AttributeValue().withBOOL(Boolean.FALSE)), + new AttributeValue().withM(floydMap)); + + List nestedList = + Arrays.asList( + new AttributeValue().withN("5"), + new AttributeValue().withN("4"), + new AttributeValue().withN("3"), + new AttributeValue().withN("2"), + new AttributeValue().withN("1")); + Map nestedMap = new HashMap(); + nestedMap.put("True", new AttributeValue().withBOOL(Boolean.TRUE)); + nestedMap.put("List", new AttributeValue().withL(nestedList)); + nestedMap.put( + "Map", + new AttributeValue() + .withM( + Collections.singletonMap("Nested", new AttributeValue().withBOOL(Boolean.TRUE)))); + + List innerList = + Arrays.asList( + new AttributeValue().withS("ComplexList"), + new AttributeValue().withN("5"), + new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5})), + new AttributeValue().withL(floydList), + new AttributeValue().withNULL(Boolean.TRUE), + new AttributeValue().withM(nestedMap)); + + AttributeValue av = new AttributeValue(); + av.addMEntry( + "SingleMap", + new AttributeValue() + .withM(Collections.singletonMap("FOO", new AttributeValue().withS("BAR")))); + av.addMEntry("InnerList", new AttributeValue().withL(innerList)); + av.addMEntry("StringSet", new AttributeValue().withSS("foo", "bar", "baz")); + return av; + } + + private void assertEquals(AttributeValue o1, AttributeValue o2) { + Assert.assertEquals(o1.getB(), o2.getB()); + assertSetsEqual(o1.getBS(), o2.getBS()); + Assert.assertEquals(o1.getN(), o2.getN()); + assertSetsEqual(o1.getNS(), o2.getNS()); + Assert.assertEquals(o1.getS(), o2.getS()); + assertSetsEqual(o1.getSS(), o2.getSS()); + Assert.assertEquals(o1.getBOOL(), o2.getBOOL()); + Assert.assertEquals(o1.getNULL(), o2.getNULL()); + + if (o1.getL() != null) { + Assert.assertNotNull(o2.getL()); + final List l1 = o1.getL(); + final List l2 = o2.getL(); + Assert.assertEquals(l1.size(), l2.size()); + for (int x = 0; x < l1.size(); ++x) { + assertEquals(l1.get(x), l2.get(x)); + } } - // This test ensures that an AttributeValue marshalled by an older - // version of this library still unmarshalls correctly. It also - // ensures that old and new marshalling is identical. - @Test - public void testVersioningCompatibility() { - AttributeValue newObject = buildComplexAttributeValue(); - byte[] oldBytes = Base64.decode(COMPLEX_ATTRIBUTE_MARSHALLED); - byte[] newBytes = marshall(newObject).array(); - AssertJUnit.assertArrayEquals(oldBytes, newBytes); - - AttributeValue oldObject = unmarshall(ByteBuffer.wrap(oldBytes)); - assertEquals(oldObject, newObject); + if (o1.getM() != null) { + Assert.assertNotNull(o2.getM()); + final Map m1 = o1.getM(); + final Map m2 = o2.getM(); + Assert.assertEquals(m1.size(), m2.size()); + for (Map.Entry entry : m1.entrySet()) { + assertEquals(entry.getValue(), m2.get(entry.getKey())); + } } - - private static final String COMPLEX_ATTRIBUTE_MARSHALLED = "AE0AAAADAHM" + - "AAAAJSW5uZXJMaXN0AEwAAAAGAHMAAAALQ29tcGxleExpc3QAbgAAAAE1AGIAA" + - "AAGAAECAwQFAEwAAAAFAD8BAAAAAABMAAAAAQA/AABNAAAAAwBzAAAABFBpbms" + - "AcwAAAAVGbG95ZABzAAAABFRlc3QAPwEAcwAAAAdWZXJzaW9uAG4AAAABMQAAA" + - "E0AAAADAHMAAAAETGlzdABMAAAABQBuAAAAATUAbgAAAAE0AG4AAAABMwBuAAA" + - "AATIAbgAAAAExAHMAAAADTWFwAE0AAAABAHMAAAAGTmVzdGVkAD8BAHMAAAAEV" + - "HJ1ZQA/AQBzAAAACVNpbmdsZU1hcABNAAAAAQBzAAAAA0ZPTwBzAAAAA0JBUgB" + - "zAAAACVN0cmluZ1NldABTAAAAAwAAAANiYXIAAAADYmF6AAAAA2Zvbw=="; - - private static AttributeValue buildComplexAttributeValue() { - Map floydMap = new HashMap(); - floydMap.put("Pink", new AttributeValue().withS("Floyd")); - floydMap.put("Version", new AttributeValue().withN("1")); - floydMap.put("Test", new AttributeValue().withBOOL(Boolean.TRUE)); - List floydList = Arrays.asList( - new AttributeValue().withBOOL(Boolean.TRUE), - new AttributeValue().withNULL(Boolean.TRUE), - new AttributeValue().withNULL(Boolean.TRUE), - new AttributeValue().withL(new AttributeValue().withBOOL(Boolean.FALSE)), - new AttributeValue().withM(floydMap) - ); - - List nestedList = Arrays.asList( - new AttributeValue().withN("5"), - new AttributeValue().withN("4"), - new AttributeValue().withN("3"), - new AttributeValue().withN("2"), - new AttributeValue().withN("1") - ); - Map nestedMap = new HashMap(); - nestedMap.put("True", new AttributeValue().withBOOL(Boolean.TRUE)); - nestedMap.put("List", new AttributeValue().withL(nestedList)); - nestedMap.put("Map", new AttributeValue().withM( - Collections.singletonMap("Nested", - new AttributeValue().withBOOL(Boolean.TRUE)))); - - List innerList = Arrays.asList( - new AttributeValue().withS("ComplexList"), - new AttributeValue().withN("5"), - new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5})), - new AttributeValue().withL(floydList), - new AttributeValue().withNULL(Boolean.TRUE), - new AttributeValue().withM(nestedMap) - ); - - AttributeValue av = new AttributeValue(); - av.addMEntry("SingleMap", new AttributeValue().withM( - Collections.singletonMap("FOO", new AttributeValue().withS("BAR")))); - av.addMEntry("InnerList", new AttributeValue().withL(innerList)); - av.addMEntry("StringSet", new AttributeValue().withSS("foo", "bar", "baz")); - return av; - } - - private void assertEquals(AttributeValue o1, AttributeValue o2) { - Assert.assertEquals(o1.getB(), o2.getB()); - assertSetsEqual(o1.getBS(), o2.getBS()); - Assert.assertEquals(o1.getN(), o2.getN()); - assertSetsEqual(o1.getNS(), o2.getNS()); - Assert.assertEquals(o1.getS(), o2.getS()); - assertSetsEqual(o1.getSS(), o2.getSS()); - Assert.assertEquals(o1.getBOOL(), o2.getBOOL()); - Assert.assertEquals(o1.getNULL(), o2.getNULL()); - - if (o1.getL() != null) { - Assert.assertNotNull(o2.getL()); - final List l1 = o1.getL(); - final List l2 = o2.getL(); - Assert.assertEquals(l1.size(), l2.size()); - for (int x = 0; x < l1.size(); ++x) { - assertEquals(l1.get(x), l2.get(x)); - } - } - - if (o1.getM() != null) { - Assert.assertNotNull(o2.getM()); - final Map m1 = o1.getM(); - final Map m2 = o2.getM(); - Assert.assertEquals(m1.size(), m2.size()); - for (Map.Entry entry : m1.entrySet()) { - assertEquals(entry.getValue(), m2.get(entry.getKey())); - } - } - } - - private void assertSetsEqual(Collection c1, Collection c2) { - Assert.assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - Assert.assertEquals(s1, s2); - } + } + + private void assertSetsEqual(Collection c1, Collection c2) { + Assert.assertFalse(c1 == null ^ c2 == null); + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + Assert.assertEquals(s1, s2); } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64Tests.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64Tests.java index c26df581..19787d03 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64Tests.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Base64Tests.java @@ -14,12 +14,6 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import org.apache.commons.lang3.StringUtils; -import org.testng.annotations.Test; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.quicktheories.QuickTheory.qt; @@ -27,78 +21,89 @@ import static org.quicktheories.generators.Generate.bytes; import static org.quicktheories.generators.SourceDSL.integers; -/** - * Tests for the Base64 interface used by the DynamoDBEncryptionClient - */ +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import org.apache.commons.lang3.StringUtils; +import org.testng.annotations.Test; + +/** Tests for the Base64 interface used by the DynamoDBEncryptionClient */ public class Base64Tests { - @Test - public void testBase64EncodeEquivalence() { - qt().forAll(byteArrays(integers().between(0, 1000000), - bytes(Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0))) - .check((a) -> { - // Base64 encode using both implementations and check for equality of output - // in case one version produces different output - String sdk1Base64 = com.amazonaws.util.Base64.encodeAsString(a); - String encryptionClientBase64 = Base64.encodeToString(a); - return StringUtils.equals(sdk1Base64, encryptionClientBase64); - }); - } + @Test + public void testBase64EncodeEquivalence() { + qt().forAll( + byteArrays( + integers().between(0, 1000000), bytes(Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0))) + .check( + (a) -> { + // Base64 encode using both implementations and check for equality of output + // in case one version produces different output + String sdk1Base64 = com.amazonaws.util.Base64.encodeAsString(a); + String encryptionClientBase64 = Base64.encodeToString(a); + return StringUtils.equals(sdk1Base64, encryptionClientBase64); + }); + } - @Test - public void testBase64DecodeEquivalence() { - qt().forAll(byteArrays(integers().between(0, 10000), - bytes(Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0))) - .as((b) -> java.util.Base64.getMimeEncoder().encodeToString(b)) - .check((s) -> { - // Check for equality using the MimeEncoder, which inserts newlines - // The encryptionClient's decoder is expected to ignore them - byte[] sdk1Bytes = com.amazonaws.util.Base64.decode(s); - byte[] encryptionClientBase64 = Base64.decode(s); - return Arrays.equals(sdk1Bytes, encryptionClientBase64); - }); - } + @Test + public void testBase64DecodeEquivalence() { + qt().forAll( + byteArrays( + integers().between(0, 10000), bytes(Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0))) + .as((b) -> java.util.Base64.getMimeEncoder().encodeToString(b)) + .check( + (s) -> { + // Check for equality using the MimeEncoder, which inserts newlines + // The encryptionClient's decoder is expected to ignore them + byte[] sdk1Bytes = com.amazonaws.util.Base64.decode(s); + byte[] encryptionClientBase64 = Base64.decode(s); + return Arrays.equals(sdk1Bytes, encryptionClientBase64); + }); + } - @Test - public void testNullDecodeBehavior() { - byte[] decoded = Base64.decode(null); - assertThat(decoded, equalTo(null)); - } + @Test + public void testNullDecodeBehavior() { + byte[] decoded = Base64.decode(null); + assertThat(decoded, equalTo(null)); + } - @Test - public void testNullDecodeBehaviorSdk1() { - byte[] decoded = com.amazonaws.util.Base64.decode((String) null); - assertThat(decoded, equalTo(null)); + @Test + public void testNullDecodeBehaviorSdk1() { + byte[] decoded = com.amazonaws.util.Base64.decode((String) null); + assertThat(decoded, equalTo(null)); - byte[] decoded2 = com.amazonaws.util.Base64.decode((byte[]) null); - assertThat(decoded2, equalTo(null)); - } + byte[] decoded2 = com.amazonaws.util.Base64.decode((byte[]) null); + assertThat(decoded2, equalTo(null)); + } - @Test - public void testBase64PaddingBehavior() { - String testInput = "another one bites the dust"; - String expectedEncoding = "YW5vdGhlciBvbmUgYml0ZXMgdGhlIGR1c3Q="; - assertThat(Base64.encodeToString(testInput.getBytes(StandardCharsets.UTF_8)), equalTo(expectedEncoding)); + @Test + public void testBase64PaddingBehavior() { + String testInput = "another one bites the dust"; + String expectedEncoding = "YW5vdGhlciBvbmUgYml0ZXMgdGhlIGR1c3Q="; + assertThat( + Base64.encodeToString(testInput.getBytes(StandardCharsets.UTF_8)), + equalTo(expectedEncoding)); - String encodingWithoutPadding = "YW5vdGhlciBvbmUgYml0ZXMgdGhlIGR1c3Q"; - assertThat(Base64.decode(encodingWithoutPadding), equalTo(testInput.getBytes())); - } + String encodingWithoutPadding = "YW5vdGhlciBvbmUgYml0ZXMgdGhlIGR1c3Q"; + assertThat(Base64.decode(encodingWithoutPadding), equalTo(testInput.getBytes())); + } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBase64PaddingBehaviorSdk1() { - String testInput = "another one bites the dust"; - String encodingWithoutPadding = "YW5vdGhlciBvbmUgYml0ZXMgdGhlIGR1c3Q"; - com.amazonaws.util.Base64.decode(encodingWithoutPadding); - } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testBase64PaddingBehaviorSdk1() { + String testInput = "another one bites the dust"; + String encodingWithoutPadding = "YW5vdGhlciBvbmUgYml0ZXMgdGhlIGR1c3Q"; + com.amazonaws.util.Base64.decode(encodingWithoutPadding); + } - @Test - public void rfc4648TestVectors() { - assertThat(Base64.encodeToString("".getBytes(StandardCharsets.UTF_8)), equalTo("")); - assertThat(Base64.encodeToString("f".getBytes(StandardCharsets.UTF_8)), equalTo("Zg==")); - assertThat(Base64.encodeToString("fo".getBytes(StandardCharsets.UTF_8)), equalTo("Zm8=")); - assertThat(Base64.encodeToString("foo".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9v")); - assertThat(Base64.encodeToString("foob".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9vYg==")); - assertThat(Base64.encodeToString("fooba".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9vYmE=")); - assertThat(Base64.encodeToString("foobar".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9vYmFy")); - } + @Test + public void rfc4648TestVectors() { + assertThat(Base64.encodeToString("".getBytes(StandardCharsets.UTF_8)), equalTo("")); + assertThat(Base64.encodeToString("f".getBytes(StandardCharsets.UTF_8)), equalTo("Zg==")); + assertThat(Base64.encodeToString("fo".getBytes(StandardCharsets.UTF_8)), equalTo("Zm8=")); + assertThat(Base64.encodeToString("foo".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9v")); + assertThat(Base64.encodeToString("foob".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9vYg==")); + assertThat( + Base64.encodeToString("fooba".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9vYmE=")); + assertThat( + Base64.encodeToString("foobar".getBytes(StandardCharsets.UTF_8)), equalTo("Zm9vYmFy")); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStreamTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStreamTest.java index cfd962e1..0442ac7c 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStreamTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ByteBufferInputStreamTest.java @@ -14,72 +14,75 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.nio.ByteBuffer; - import static org.testng.AssertJUnit.assertArrayEquals; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; +import java.io.IOException; +import java.nio.ByteBuffer; +import org.testng.annotations.Test; + public class ByteBufferInputStreamTest { - @Test - public void testRead() throws IOException { - ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - for (int x = 0; x < 10; ++x) { - assertEquals(10 - x, bis.available()); - assertEquals(x, bis.read()); - } - assertEquals(0, bis.available()); - bis.close(); + @Test + public void testRead() throws IOException { + ByteBufferInputStream bis = + new ByteBufferInputStream(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + for (int x = 0; x < 10; ++x) { + assertEquals(10 - x, bis.available()); + assertEquals(x, bis.read()); } + assertEquals(0, bis.available()); + bis.close(); + } - @Test - public void testReadByteArray() throws IOException { - ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - assertEquals(10, bis.available()); + @Test + public void testReadByteArray() throws IOException { + ByteBufferInputStream bis = + new ByteBufferInputStream(ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + assertEquals(10, bis.available()); - byte[] buff = new byte[4]; + byte[] buff = new byte[4]; - int len = bis.read(buff); - assertEquals(4, len); - assertEquals(6, bis.available()); - assertArrayEquals(new byte[]{0, 1, 2, 3}, buff); + int len = bis.read(buff); + assertEquals(4, len); + assertEquals(6, bis.available()); + assertArrayEquals(new byte[] {0, 1, 2, 3}, buff); - len = bis.read(buff); - assertEquals(4, len); - assertEquals(2, bis.available()); - assertArrayEquals(new byte[]{4, 5, 6, 7}, buff); + len = bis.read(buff); + assertEquals(4, len); + assertEquals(2, bis.available()); + assertArrayEquals(new byte[] {4, 5, 6, 7}, buff); - len = bis.read(buff); - assertEquals(2, len); - assertEquals(0, bis.available()); - assertArrayEquals(new byte[]{8, 9, 6, 7}, buff); - bis.close(); - } + len = bis.read(buff); + assertEquals(2, len); + assertEquals(0, bis.available()); + assertArrayEquals(new byte[] {8, 9, 6, 7}, buff); + bis.close(); + } - @Test - public void testSkip() throws IOException { - ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.wrap(new byte[]{(byte) 0xFA, 15, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - assertEquals(13, bis.available()); - assertEquals(0xFA, bis.read()); - assertEquals(12, bis.available()); - bis.skip(2); - assertEquals(10, bis.available()); - for (int x = 0; x < 10; ++x) { - assertEquals(x, bis.read()); - } - assertEquals(0, bis.available()); - assertEquals(-1, bis.read()); - bis.close(); + @Test + public void testSkip() throws IOException { + ByteBufferInputStream bis = + new ByteBufferInputStream( + ByteBuffer.wrap(new byte[] {(byte) 0xFA, 15, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + assertEquals(13, bis.available()); + assertEquals(0xFA, bis.read()); + assertEquals(12, bis.available()); + bis.skip(2); + assertEquals(10, bis.available()); + for (int x = 0; x < 10; ++x) { + assertEquals(x, bis.read()); } + assertEquals(0, bis.available()); + assertEquals(-1, bis.read()); + bis.close(); + } - @Test - public void testMarkSupported() throws IOException { - try (ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.allocate(0))) { - assertFalse(bis.markSupported()); - } + @Test + public void testMarkSupported() throws IOException { + try (ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.allocate(0))) { + assertFalse(bis.markSupported()); } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ConcurrentTTLCacheTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ConcurrentTTLCacheTest.java index ee27364e..6d40d481 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ConcurrentTTLCacheTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/ConcurrentTTLCacheTest.java @@ -1,235 +1,244 @@ package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import edu.umd.cs.mtc.MultithreadedTestCase; -import edu.umd.cs.mtc.TestFramework; -import org.testng.annotations.Test; - -import java.util.concurrent.TimeUnit; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import edu.umd.cs.mtc.MultithreadedTestCase; +import edu.umd.cs.mtc.TestFramework; +import java.util.concurrent.TimeUnit; +import org.testng.annotations.Test; + /* Test specific thread interleavings with behaviors we care about in the * TTLCache. */ public class ConcurrentTTLCacheTest { - private static final long TTL_GRACE_IN_NANO = TimeUnit.MILLISECONDS.toNanos(500); - private static final long ttlInMillis = 1000; + private static final long TTL_GRACE_IN_NANO = TimeUnit.MILLISECONDS.toNanos(500); + private static final long ttlInMillis = 1000; + + @Test + public void testGracePeriodCase() throws Throwable { + TestFramework.runOnce(new GracePeriodCase()); + } + + @Test + public void testExpiredCase() throws Throwable { + TestFramework.runOnce(new ExpiredCase()); + } + + @Test + public void testNewEntryCase() throws Throwable { + TestFramework.runOnce(new NewEntryCase()); + } + + @Test + public void testPutLoadCase() throws Throwable { + TestFramework.runOnce(new PutLoadCase()); + } + + // Ensure the loader is only called once if two threads attempt to load during the grace period + class GracePeriodCase extends MultithreadedTestCase { + TTLCache cache; + TTLCache.EntryLoader loader; + MsClock clock = mock(MsClock.class); + + @Override + public void initialize() { + loader = + spy( + new TTLCache.EntryLoader() { + @Override + public String load(String entryKey) { + // Wait until thread2 finishes to complete load + waitForTick(2); + return "loadedValue"; + } + }); + when(clock.timestampNano()).thenReturn((long) 0); + cache = new TTLCache<>(3, ttlInMillis, loader); + cache.clock = clock; - @Test - public void testGracePeriodCase() throws Throwable { - TestFramework.runOnce(new GracePeriodCase()); + // Put an initial value into the cache at time 0 + cache.put("k1", "v1"); } - @Test - public void testExpiredCase() throws Throwable { - TestFramework.runOnce(new ExpiredCase()); + // The thread that first calls load in the grace period and acquires the lock + public void thread1() { + when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + 1); + String loadedValue = cache.load("k1"); + assertTick(2); + // Expect to get back the value calculated from load + assertEquals("loadedValue", loadedValue); } - @Test - public void testNewEntryCase() throws Throwable { - TestFramework.runOnce(new NewEntryCase()); + // The thread that calls load in the grace period after the lock has been acquired + public void thread2() { + // Wait until the first thread acquires the lock and starts load + waitForTick(1); + when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + 1); + String loadedValue = cache.load("k1"); + // Expect to get back the original value in the cache + assertEquals("v1", loadedValue); } - @Test - public void testPutLoadCase() throws Throwable { - TestFramework.runOnce(new PutLoadCase()); + @Override + public void finish() { + // Ensure the loader was only called once + verify(loader, times(1)).load("k1"); } - - // Ensure the loader is only called once if two threads attempt to load during the grace period - class GracePeriodCase extends MultithreadedTestCase { - TTLCache cache; - TTLCache.EntryLoader loader; - MsClock clock = mock(MsClock.class); - - @Override - public void initialize() { - loader = spy(new TTLCache.EntryLoader() { + } + + // Ensure the loader is only called once if two threads attempt to load an expired entry. + class ExpiredCase extends MultithreadedTestCase { + TTLCache cache; + TTLCache.EntryLoader loader; + MsClock clock = mock(MsClock.class); + + @Override + public void initialize() { + loader = + spy( + new TTLCache.EntryLoader() { @Override public String load(String entryKey) { - // Wait until thread2 finishes to complete load - waitForTick(2); - return "loadedValue"; + // Wait until thread2 is waiting for the lock to complete load + waitForTick(2); + return "loadedValue"; } - }); - when(clock.timestampNano()).thenReturn((long) 0); - cache = new TTLCache<>(3, ttlInMillis, loader); - cache.clock = clock; - - // Put an initial value into the cache at time 0 - cache.put("k1", "v1"); - } - - // The thread that first calls load in the grace period and acquires the lock - public void thread1() { - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + 1); - String loadedValue = cache.load("k1"); - assertTick(2); - // Expect to get back the value calculated from load - assertEquals("loadedValue", loadedValue); - } - - // The thread that calls load in the grace period after the lock has been acquired - public void thread2() { - // Wait until the first thread acquires the lock and starts load - waitForTick(1); - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + 1); - String loadedValue = cache.load("k1"); - // Expect to get back the original value in the cache - assertEquals("v1", loadedValue); - } - - @Override - public void finish() { - // Ensure the loader was only called once - verify(loader, times(1)).load("k1"); - } + }); + when(clock.timestampNano()).thenReturn((long) 0); + cache = new TTLCache<>(3, ttlInMillis, loader); + cache.clock = clock; + + // Put an initial value into the cache at time 0 + cache.put("k1", "v1"); } - // Ensure the loader is only called once if two threads attempt to load an expired entry. - class ExpiredCase extends MultithreadedTestCase { - TTLCache cache; - TTLCache.EntryLoader loader; - MsClock clock = mock(MsClock.class); + // The thread that first calls load after expiration + public void thread1() { + when(clock.timestampNano()) + .thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); + String loadedValue = cache.load("k1"); + assertTick(2); + // Expect to get back the value calculated from load + assertEquals("loadedValue", loadedValue); + } - @Override - public void initialize() { - loader = spy(new TTLCache.EntryLoader() { + // The thread that calls load after expiration, + // after the first thread calls load, but before + // the new value is put into the cache. + public void thread2() { + // Wait until the first thread acquires the lock and starts load + waitForTick(1); + when(clock.timestampNano()) + .thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); + String loadedValue = cache.load("k1"); + // Expect to get back the newly loaded value + assertEquals("loadedValue", loadedValue); + // assert that this thread only finishes once the first thread's load does + assertTick(2); + } + + @Override + public void finish() { + // Ensure the loader was only called once + verify(loader, times(1)).load("k1"); + } + } + + // Ensure the loader is only called once if two threads attempt to load the same new entry. + class NewEntryCase extends MultithreadedTestCase { + TTLCache cache; + TTLCache.EntryLoader loader; + MsClock clock = mock(MsClock.class); + + @Override + public void initialize() { + loader = + spy( + new TTLCache.EntryLoader() { @Override public String load(String entryKey) { - // Wait until thread2 is waiting for the lock to complete load - waitForTick(2); - return "loadedValue"; + // Wait until thread2 is blocked to complete load + waitForTick(2); + return "loadedValue"; } - }); - when(clock.timestampNano()).thenReturn((long) 0); - cache = new TTLCache<>(3, ttlInMillis, loader); - cache.clock = clock; - - // Put an initial value into the cache at time 0 - cache.put("k1", "v1"); - } - - // The thread that first calls load after expiration - public void thread1() { - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); - String loadedValue = cache.load("k1"); - assertTick(2); - // Expect to get back the value calculated from load - assertEquals("loadedValue", loadedValue); - } - - // The thread that calls load after expiration, - // after the first thread calls load, but before - // the new value is put into the cache. - public void thread2() { - // Wait until the first thread acquires the lock and starts load - waitForTick(1); - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); - String loadedValue = cache.load("k1"); - // Expect to get back the newly loaded value - assertEquals("loadedValue", loadedValue); - // assert that this thread only finishes once the first thread's load does - assertTick(2); - } - - @Override - public void finish() { - // Ensure the loader was only called once - verify(loader, times(1)).load("k1"); - } + }); + when(clock.timestampNano()).thenReturn((long) 0); + cache = new TTLCache<>(3, ttlInMillis, loader); + cache.clock = clock; + } + + // The thread that first calls load + public void thread1() { + String loadedValue = cache.load("k1"); + assertTick(2); + // Expect to get back the value calculated from load + assertEquals("loadedValue", loadedValue); } - // Ensure the loader is only called once if two threads attempt to load the same new entry. - class NewEntryCase extends MultithreadedTestCase { - TTLCache cache; - TTLCache.EntryLoader loader; - MsClock clock = mock(MsClock.class); + // The thread that calls load after the first thread calls load, + // but before the new value is put into the cache. + public void thread2() { + // Wait until the first thread acquires the lock and starts load + waitForTick(1); + String loadedValue = cache.load("k1"); + // Expect to get back the newly loaded value + assertEquals("loadedValue", loadedValue); + // assert that this thread only finishes once the first thread's load does + assertTick(2); + } - @Override - public void initialize() { - loader = spy(new TTLCache.EntryLoader() { + @Override + public void finish() { + // Ensure the loader was only called once + verify(loader, times(1)).load("k1"); + } + } + + // Ensure the loader blocks put on load/put of the same new entry + class PutLoadCase extends MultithreadedTestCase { + TTLCache cache; + TTLCache.EntryLoader loader; + MsClock clock = mock(MsClock.class); + + @Override + public void initialize() { + loader = + spy( + new TTLCache.EntryLoader() { @Override public String load(String entryKey) { - // Wait until thread2 is blocked to complete load - waitForTick(2); - return "loadedValue"; + // Wait until the put blocks to complete load + waitForTick(2); + return "loadedValue"; } - }); - when(clock.timestampNano()).thenReturn((long) 0); - cache = new TTLCache<>(3, ttlInMillis, loader); - cache.clock = clock; - } - - // The thread that first calls load - public void thread1() { - String loadedValue = cache.load("k1"); - assertTick(2); - // Expect to get back the value calculated from load - assertEquals("loadedValue", loadedValue); - } - - // The thread that calls load after the first thread calls load, - // but before the new value is put into the cache. - public void thread2() { - // Wait until the first thread acquires the lock and starts load - waitForTick(1); - String loadedValue = cache.load("k1"); - // Expect to get back the newly loaded value - assertEquals("loadedValue", loadedValue); - // assert that this thread only finishes once the first thread's load does - assertTick(2); - } - - @Override - public void finish() { - // Ensure the loader was only called once - verify(loader, times(1)).load("k1"); - } + }); + when(clock.timestampNano()).thenReturn((long) 0); + cache = new TTLCache<>(3, ttlInMillis, loader); + cache.clock = clock; } - // Ensure the loader blocks put on load/put of the same new entry - class PutLoadCase extends MultithreadedTestCase { - TTLCache cache; - TTLCache.EntryLoader loader; - MsClock clock = mock(MsClock.class); + // The thread that first calls load + public void thread1() { + String loadedValue = cache.load("k1"); + // Expect to get back the value calculated from load + assertEquals("loadedValue", loadedValue); + verify(loader, times(1)).load("k1"); + } - @Override - public void initialize() { - loader = spy(new TTLCache.EntryLoader() { - @Override - public String load(String entryKey) { - // Wait until the put blocks to complete load - waitForTick(2); - return "loadedValue"; - } - }); - when(clock.timestampNano()).thenReturn((long) 0); - cache = new TTLCache<>(3, ttlInMillis, loader); - cache.clock = clock; - } - - // The thread that first calls load - public void thread1() { - String loadedValue = cache.load("k1"); - // Expect to get back the value calculated from load - assertEquals("loadedValue", loadedValue); - verify(loader, times(1)).load("k1"); - } - - // The thread that calls put during the first thread's load - public void thread2() { - // Wait until the first thread is loading - waitForTick(1); - String previousValue = cache.put("k1", "v1"); - // Expect to get back the value loaded into the cache by thread1 - assertEquals("loadedValue", previousValue); - // assert that this thread was blocked by the first thread - assertTick(2); - } + // The thread that calls put during the first thread's load + public void thread2() { + // Wait until the first thread is loading + waitForTick(1); + String previousValue = cache.put("k1", "v1"); + // Expect to get back the value loaded into the cache by thread1 + assertEquals("loadedValue", previousValue); + // assert that this thread was blocked by the first thread + assertTick(2); } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/HkdfTests.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/HkdfTests.java index b813ad71..829302ff 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/HkdfTests.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/HkdfTests.java @@ -12,180 +12,198 @@ */ package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import org.testng.annotations.Test; - import static org.testng.AssertJUnit.assertArrayEquals; +import org.testng.annotations.Test; + public class HkdfTests { - private static final testCase[] testCases = new testCase[]{ - new testCase( - "HmacSHA256", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" - + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"), - fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"), - fromHex("3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865")), - new testCase( - "HmacSHA256", - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d" - + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b" - + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29" - + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37" - + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45" - + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"), - fromCHex("\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d" - + "\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b" - + "\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89" - + "\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97" - + "\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5" - + "\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf"), - fromCHex("\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd" - + "\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb" - + "\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9" - + "\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7" - + "\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5" - + "\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff"), - fromHex("B11E398DC80327A1C8E7F78C596A4934" - + "4F012EDA2D4EFAD8A050CC4C19AFA97C" - + "59045A99CAC7827271CB41C65E590E09" - + "DA3275600C2F09B8367793A9ACA3DB71" - + "CC30C58179EC3E87C14C01D5C1F3434F" + "1D87")), - new testCase( - "HmacSHA256", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" - + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - new byte[0], new byte[0], - fromHex("8DA4E775A563C18F715F802A063C5A31" - + "B8A11F5C5EE1879EC3454E5F3C738D2D" - + "9D201395FAA4B61A96C8")), - new testCase( - "HmacSHA1", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"), - fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"), - fromHex("085A01EA1B10F36933068B56EFA5AD81" - + "A4F14B822F5B091568A9CDD4F155FDA2" - + "C22E422478D305F3F896")), - new testCase( - "HmacSHA1", - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d" - + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b" - + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29" - + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37" - + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45" - + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"), - fromCHex("\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6A\\x6B\\x6C\\x6D" - + "\\x6E\\x6F\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7A\\x7B" - + "\\x7C\\x7D\\x7E\\x7F\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89" - + "\\x8A\\x8B\\x8C\\x8D\\x8E\\x8F\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97" - + "\\x98\\x99\\x9A\\x9B\\x9C\\x9D\\x9E\\x9F\\xA0\\xA1\\xA2\\xA3\\xA4\\xA5" - + "\\xA6\\xA7\\xA8\\xA9\\xAA\\xAB\\xAC\\xAD\\xAE\\xAF"), - fromCHex("\\xB0\\xB1\\xB2\\xB3\\xB4\\xB5\\xB6\\xB7\\xB8\\xB9\\xBA\\xBB\\xBC\\xBD" - + "\\xBE\\xBF\\xC0\\xC1\\xC2\\xC3\\xC4\\xC5\\xC6\\xC7\\xC8\\xC9\\xCA\\xCB" - + "\\xCC\\xCD\\xCE\\xCF\\xD0\\xD1\\xD2\\xD3\\xD4\\xD5\\xD6\\xD7\\xD8\\xD9" - + "\\xDA\\xDB\\xDC\\xDD\\xDE\\xDF\\xE0\\xE1\\xE2\\xE3\\xE4\\xE5\\xE6\\xE7" - + "\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\\xF0\\xF1\\xF2\\xF3\\xF4\\xF5" - + "\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF"), - fromHex("0BD770A74D1160F7C9F12CD5912A06EB" - + "FF6ADCAE899D92191FE4305673BA2FFE" - + "8FA3F1A4E5AD79F3F334B3B202B2173C" - + "486EA37CE3D397ED034C7F9DFEB15C5E" - + "927336D0441F4C4300E2CFF0D0900B52D3B4")), - new testCase( - "HmacSHA1", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" - + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - new byte[0], new byte[0], - fromHex("0AC1AF7002B3D761D1E55298DA9D0506" - + "B9AE52057220A306E07B6B87E8DF21D0")), - new testCase( - "HmacSHA1", - fromCHex("\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c" - + "\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c"), - null, new byte[0], - fromHex("2C91117204D745F3500D636A62F64F0A" - + "B3BAE548AA53D423B0D1F27EBBA6F5E5" - + "673A081D70CCE7ACFC48"))}; + private static final testCase[] testCases = + new testCase[] { + new testCase( + "HmacSHA256", + fromCHex( + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" + + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), + fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"), + fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"), + fromHex( + "3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865")), + new testCase( + "HmacSHA256", + fromCHex( + "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d" + + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b" + + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29" + + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37" + + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45" + + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"), + fromCHex( + "\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d" + + "\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b" + + "\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89" + + "\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97" + + "\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5" + + "\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf"), + fromCHex( + "\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd" + + "\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb" + + "\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9" + + "\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7" + + "\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5" + + "\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff"), + fromHex( + "B11E398DC80327A1C8E7F78C596A4934" + + "4F012EDA2D4EFAD8A050CC4C19AFA97C" + + "59045A99CAC7827271CB41C65E590E09" + + "DA3275600C2F09B8367793A9ACA3DB71" + + "CC30C58179EC3E87C14C01D5C1F3434F" + + "1D87")), + new testCase( + "HmacSHA256", + fromCHex( + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" + + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), + new byte[0], + new byte[0], + fromHex( + "8DA4E775A563C18F715F802A063C5A31" + + "B8A11F5C5EE1879EC3454E5F3C738D2D" + + "9D201395FAA4B61A96C8")), + new testCase( + "HmacSHA1", + fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), + fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"), + fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"), + fromHex( + "085A01EA1B10F36933068B56EFA5AD81" + + "A4F14B822F5B091568A9CDD4F155FDA2" + + "C22E422478D305F3F896")), + new testCase( + "HmacSHA1", + fromCHex( + "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d" + + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b" + + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29" + + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37" + + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45" + + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"), + fromCHex( + "\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6A\\x6B\\x6C\\x6D" + + "\\x6E\\x6F\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7A\\x7B" + + "\\x7C\\x7D\\x7E\\x7F\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89" + + "\\x8A\\x8B\\x8C\\x8D\\x8E\\x8F\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97" + + "\\x98\\x99\\x9A\\x9B\\x9C\\x9D\\x9E\\x9F\\xA0\\xA1\\xA2\\xA3\\xA4\\xA5" + + "\\xA6\\xA7\\xA8\\xA9\\xAA\\xAB\\xAC\\xAD\\xAE\\xAF"), + fromCHex( + "\\xB0\\xB1\\xB2\\xB3\\xB4\\xB5\\xB6\\xB7\\xB8\\xB9\\xBA\\xBB\\xBC\\xBD" + + "\\xBE\\xBF\\xC0\\xC1\\xC2\\xC3\\xC4\\xC5\\xC6\\xC7\\xC8\\xC9\\xCA\\xCB" + + "\\xCC\\xCD\\xCE\\xCF\\xD0\\xD1\\xD2\\xD3\\xD4\\xD5\\xD6\\xD7\\xD8\\xD9" + + "\\xDA\\xDB\\xDC\\xDD\\xDE\\xDF\\xE0\\xE1\\xE2\\xE3\\xE4\\xE5\\xE6\\xE7" + + "\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\\xF0\\xF1\\xF2\\xF3\\xF4\\xF5" + + "\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF"), + fromHex( + "0BD770A74D1160F7C9F12CD5912A06EB" + + "FF6ADCAE899D92191FE4305673BA2FFE" + + "8FA3F1A4E5AD79F3F334B3B202B2173C" + + "486EA37CE3D397ED034C7F9DFEB15C5E" + + "927336D0441F4C4300E2CFF0D0900B52D3B4")), + new testCase( + "HmacSHA1", + fromCHex( + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" + + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), + new byte[0], + new byte[0], + fromHex("0AC1AF7002B3D761D1E55298DA9D0506" + "B9AE52057220A306E07B6B87E8DF21D0")), + new testCase( + "HmacSHA1", + fromCHex( + "\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c" + + "\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c"), + null, + new byte[0], + fromHex( + "2C91117204D745F3500D636A62F64F0A" + + "B3BAE548AA53D423B0D1F27EBBA6F5E5" + + "673A081D70CCE7ACFC48")) + }; - @Test - public void rfc5869Tests() throws Exception { - for (int x = 0; x < testCases.length; x++) { - testCase trial = testCases[x]; - System.out.println("Test case A." + (x + 1)); - Hkdf kdf = Hkdf.getInstance(trial.algo); - kdf.init(trial.ikm, trial.salt); - byte[] result = kdf.deriveKey(trial.info, trial.expected.length); - assertArrayEquals("Trial A." + x, trial.expected, result); - } + @Test + public void rfc5869Tests() throws Exception { + for (int x = 0; x < testCases.length; x++) { + testCase trial = testCases[x]; + System.out.println("Test case A." + (x + 1)); + Hkdf kdf = Hkdf.getInstance(trial.algo); + kdf.init(trial.ikm, trial.salt); + byte[] result = kdf.deriveKey(trial.info, trial.expected.length); + assertArrayEquals("Trial A." + x, trial.expected, result); } + } - @Test - public void nullTests() throws Exception { - testCase trial = testCases[0]; - Hkdf kdf = Hkdf.getInstance(trial.algo); - kdf.init(trial.ikm, trial.salt); - // Just ensuring no exceptions are thrown - kdf.deriveKey((String) null, 16); - kdf.deriveKey((byte[]) null, 16); - } + @Test + public void nullTests() throws Exception { + testCase trial = testCases[0]; + Hkdf kdf = Hkdf.getInstance(trial.algo); + kdf.init(trial.ikm, trial.salt); + // Just ensuring no exceptions are thrown + kdf.deriveKey((String) null, 16); + kdf.deriveKey((byte[]) null, 16); + } - @Test - public void defaultSalt() throws Exception { - // Tests all the different ways to get the default salt + @Test + public void defaultSalt() throws Exception { + // Tests all the different ways to get the default salt - testCase trial = testCases[0]; - Hkdf kdf1 = Hkdf.getInstance(trial.algo); - kdf1.init(trial.ikm, null); - Hkdf kdf2 = Hkdf.getInstance(trial.algo); - kdf2.init(trial.ikm, new byte[0]); - Hkdf kdf3 = Hkdf.getInstance(trial.algo); - kdf3.init(trial.ikm); - Hkdf kdf4 = Hkdf.getInstance(trial.algo); - kdf4.init(trial.ikm, new byte[32]); + testCase trial = testCases[0]; + Hkdf kdf1 = Hkdf.getInstance(trial.algo); + kdf1.init(trial.ikm, null); + Hkdf kdf2 = Hkdf.getInstance(trial.algo); + kdf2.init(trial.ikm, new byte[0]); + Hkdf kdf3 = Hkdf.getInstance(trial.algo); + kdf3.init(trial.ikm); + Hkdf kdf4 = Hkdf.getInstance(trial.algo); + kdf4.init(trial.ikm, new byte[32]); - byte[] key1 = kdf1.deriveKey("Test", 16); - byte[] key2 = kdf2.deriveKey("Test", 16); - byte[] key3 = kdf3.deriveKey("Test", 16); - byte[] key4 = kdf4.deriveKey("Test", 16); + byte[] key1 = kdf1.deriveKey("Test", 16); + byte[] key2 = kdf2.deriveKey("Test", 16); + byte[] key3 = kdf3.deriveKey("Test", 16); + byte[] key4 = kdf4.deriveKey("Test", 16); - assertArrayEquals(key1, key2); - assertArrayEquals(key1, key3); - assertArrayEquals(key1, key4); - } + assertArrayEquals(key1, key2); + assertArrayEquals(key1, key3); + assertArrayEquals(key1, key4); + } - private static byte[] fromHex(String data) { - byte[] result = new byte[data.length() / 2]; - for (int x = 0; x < result.length; x++) { - result[x] = (byte) Integer.parseInt( - data.substring(2 * x, 2 * x + 2), 16); - } - return result; + private static byte[] fromHex(String data) { + byte[] result = new byte[data.length() / 2]; + for (int x = 0; x < result.length; x++) { + result[x] = (byte) Integer.parseInt(data.substring(2 * x, 2 * x + 2), 16); } + return result; + } - private static byte[] fromCHex(String data) { - byte[] result = new byte[data.length() / 4]; - for (int x = 0; x < result.length; x++) { - result[x] = (byte) Integer.parseInt( - data.substring(4 * x + 2, 4 * x + 4), 16); - } - return result; + private static byte[] fromCHex(String data) { + byte[] result = new byte[data.length() / 4]; + for (int x = 0; x < result.length; x++) { + result[x] = (byte) Integer.parseInt(data.substring(4 * x + 2, 4 * x + 4), 16); } + return result; + } - private static class testCase { - public final String algo; - public final byte[] ikm; - public final byte[] salt; - public final byte[] info; - public final byte[] expected; + private static class testCase { + public final String algo; + public final byte[] ikm; + public final byte[] salt; + public final byte[] info; + public final byte[] expected; - public testCase(String algo, byte[] ikm, byte[] salt, byte[] info, - byte[] expected) { - super(); - this.algo = algo; - this.ikm = ikm; - this.salt = salt; - this.info = info; - this.expected = expected; - } + public testCase(String algo, byte[] ikm, byte[] salt, byte[] info, byte[] expected) { + super(); + this.algo = algo; + this.ikm = ikm; + this.salt = salt; + this.info = info; + this.expected = expected; } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCacheTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCacheTest.java index 397ccbd7..a75fcd72 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCacheTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/LRUCacheTest.java @@ -2,110 +2,109 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import org.testng.annotations.Test; - import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; -public class LRUCacheTest { - @Test - public void test() { - final LRUCache cache = new LRUCache(3); - assertEquals(0, cache.size()); - assertEquals(3, cache.getMaxSize()); - cache.add("k1", "v1"); - assertTrue(cache.size() == 1); - cache.add("k1", "v11"); - assertTrue(cache.size() == 1); - cache.add("k2", "v2"); - assertTrue(cache.size() == 2); - cache.add("k3", "v3"); - assertTrue(cache.size() == 3); - assertEquals("v11", cache.get("k1")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - cache.add("k4", "v4"); - assertTrue(cache.size() == 3); - assertNull(cache.get("k1")); - assertEquals("v4", cache.get("k4")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - assertTrue(cache.size() == 3); - cache.add("k5", "v5"); - assertNull(cache.get("k4")); - assertEquals("v5", cache.get("k5")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - cache.clear(); - assertEquals(0, cache.size()); - } +import org.testng.annotations.Test; - @Test - public void testRemove() { - final LRUCache cache = new LRUCache(3); - assertEquals(0, cache.size()); - assertEquals(3, cache.getMaxSize()); - cache.add("k1", "v1"); - assertTrue(cache.size() == 1); - final String oldValue = cache.remove("k1"); - assertTrue(cache.size() == 0); - assertEquals("v1", oldValue); - assertNull(cache.get("k1")); +public class LRUCacheTest { + @Test + public void test() { + final LRUCache cache = new LRUCache(3); + assertEquals(0, cache.size()); + assertEquals(3, cache.getMaxSize()); + cache.add("k1", "v1"); + assertTrue(cache.size() == 1); + cache.add("k1", "v11"); + assertTrue(cache.size() == 1); + cache.add("k2", "v2"); + assertTrue(cache.size() == 2); + cache.add("k3", "v3"); + assertTrue(cache.size() == 3); + assertEquals("v11", cache.get("k1")); + assertEquals("v2", cache.get("k2")); + assertEquals("v3", cache.get("k3")); + cache.add("k4", "v4"); + assertTrue(cache.size() == 3); + assertNull(cache.get("k1")); + assertEquals("v4", cache.get("k4")); + assertEquals("v2", cache.get("k2")); + assertEquals("v3", cache.get("k3")); + assertTrue(cache.size() == 3); + cache.add("k5", "v5"); + assertNull(cache.get("k4")); + assertEquals("v5", cache.get("k5")); + assertEquals("v2", cache.get("k2")); + assertEquals("v3", cache.get("k3")); + cache.clear(); + assertEquals(0, cache.size()); + } - final String emptyValue = cache.remove("k1"); - assertTrue(cache.size() == 0); - assertNull(emptyValue); - assertNull(cache.get("k1")); - } + @Test + public void testRemove() { + final LRUCache cache = new LRUCache(3); + assertEquals(0, cache.size()); + assertEquals(3, cache.getMaxSize()); + cache.add("k1", "v1"); + assertTrue(cache.size() == 1); + final String oldValue = cache.remove("k1"); + assertTrue(cache.size() == 0); + assertEquals("v1", oldValue); + assertNull(cache.get("k1")); - @Test - public void testClear() { - final LRUCache cache = new LRUCache(3); - assertEquals(0, cache.size()); - cache.clear(); - assertEquals(0, cache.size()); + final String emptyValue = cache.remove("k1"); + assertTrue(cache.size() == 0); + assertNull(emptyValue); + assertNull(cache.get("k1")); + } - cache.add("k1", "v1"); - cache.add("k2", "v2"); - cache.add("k3", "v3"); - assertTrue(cache.size() == 3); - cache.clear(); - assertTrue(cache.size() == 0); - assertNull(cache.get("k1")); - assertNull(cache.get("k2")); - assertNull(cache.get("k3")); - } + @Test + public void testClear() { + final LRUCache cache = new LRUCache(3); + assertEquals(0, cache.size()); + cache.clear(); + assertEquals(0, cache.size()); - @Test(expectedExceptions = IllegalArgumentException.class) - public void testZeroSize() { - new LRUCache(0); - } + cache.add("k1", "v1"); + cache.add("k2", "v2"); + cache.add("k3", "v3"); + assertTrue(cache.size() == 3); + cache.clear(); + assertTrue(cache.size() == 0); + assertNull(cache.get("k1")); + assertNull(cache.get("k2")); + assertNull(cache.get("k3")); + } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testIllegalArgument() { - new LRUCache(-1); - } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testZeroSize() { + new LRUCache(0); + } - @Test - public void testSingleEntry() { - final LRUCache cache = new LRUCache(1); - assertTrue(cache.size() == 0); - cache.add("k1", "v1"); - assertTrue(cache.size() == 1); - cache.add("k1", "v11"); - assertTrue(cache.size() == 1); - assertEquals("v11", cache.get("k1")); + @Test(expectedExceptions = IllegalArgumentException.class) + public void testIllegalArgument() { + new LRUCache(-1); + } - cache.add("k2", "v2"); - assertTrue(cache.size() == 1); - assertEquals("v2", cache.get("k2")); - assertNull(cache.get("k1")); + @Test + public void testSingleEntry() { + final LRUCache cache = new LRUCache(1); + assertTrue(cache.size() == 0); + cache.add("k1", "v1"); + assertTrue(cache.size() == 1); + cache.add("k1", "v11"); + assertTrue(cache.size() == 1); + assertEquals("v11", cache.get("k1")); - cache.add("k3", "v3"); - assertTrue(cache.size() == 1); - assertEquals("v3", cache.get("k3")); - assertNull(cache.get("k2")); - } + cache.add("k2", "v2"); + assertTrue(cache.size() == 1); + assertEquals("v2", cache.get("k2")); + assertNull(cache.get("k1")); + cache.add("k3", "v3"); + assertTrue(cache.size() == 1); + assertEquals("v3", cache.get("k3")); + assertNull(cache.get("k2")); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCacheTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCacheTest.java index d0867498..5bb7bcb7 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCacheTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/TTLCacheTest.java @@ -2,11 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling.internal; -import org.testng.annotations.Test; - -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -19,346 +14,358 @@ import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import org.testng.annotations.Test; + public class TTLCacheTest { - private static final long TTL_GRACE_IN_NANO = TimeUnit.MILLISECONDS.toNanos(500); - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalidSize() { - final TTLCache cache = new TTLCache(0, 1000, mock(TTLCache.EntryLoader.class)); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalidTTL() { - final TTLCache cache = new TTLCache(3, 0, mock(TTLCache.EntryLoader.class)); - } - - @Test(expectedExceptions = NullPointerException.class) - public void testNullLoader() { - final TTLCache cache = new TTLCache(3, 1000, null); - } - - @Test - public void testConstructor() { - final TTLCache cache = new TTLCache(1000, 1000, mock(TTLCache.EntryLoader.class)); - assertEquals(0, cache.size()); - assertEquals(1000, cache.getMaxSize()); - } - - @Test - public void testLoadPastMaxSize() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 1; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - MsClock clock = mock(MsClock.class); - when(clock.timestampNano()).thenReturn((long) 0); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - cache.load("k1"); - verify(loader, times(1)).load("k1"); - assertTrue(cache.size() == 1); - - String result = cache.load("k2"); - verify(loader, times(1)).load("k2"); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, result); - - // to verify result is in the cache, load one more time - // and expect the loader to not be called - String cachedValue = cache.load("k2"); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, cachedValue); - } - - @Test - public void testLoadNoExistingEntry() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - MsClock clock = mock(MsClock.class); - when(clock.timestampNano()).thenReturn((long) 0); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - String result = cache.load("k1"); - verify(loader, times(1)).load("k1"); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, result); - - // to verify result is in the cache, load one more time - // and expect the loader to not be called - String cachedValue = cache.load("k1"); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, cachedValue); - } - - @Test - public void testLoadNotExpired() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - MsClock clock = mock(MsClock.class); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - // when first creating the entry, time is 0 - when(clock.timestampNano()).thenReturn((long) 0); - cache.load("k1"); - assertTrue(cache.size() == 1); - verify(loader, times(1)).load("k1"); - - // on load, time is within TTL - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis)); - String result = cache.load("k1"); - - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, result); - } - - @Test - public void testLoadInGrace() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - MsClock clock = mock(MsClock.class); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - // when first creating the entry, time is zero - when(clock.timestampNano()).thenReturn((long) 0); - cache.load("k1"); - assertTrue(cache.size() == 1); - verify(loader, times(1)).load("k1"); - - // on load, time is past TTL but within the grace period - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + 1); - String result = cache.load("k1"); - - // Because this is tested in a single thread, - // this is expected to obtain the lock and load the new value - verify(loader, times(2)).load("k1"); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, result); - } - - @Test - public void testLoadExpired() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - MsClock clock = mock(MsClock.class); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - // when first creating the entry, time is zero - when(clock.timestampNano()).thenReturn((long) 0); - cache.load("k1"); - assertTrue(cache.size() == 1); - verify(loader, times(1)).load("k1"); - - // on load, time is past TTL and grace period - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); - String result = cache.load("k1"); - - verify(loader, times(2)).load("k1"); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(loadedValue, result); - } - - @Test - public void testLoadExpiredEviction() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue).thenThrow(new IllegalStateException("This loader is mocked to throw a failure.")); - MsClock clock = mock(MsClock.class); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - // when first creating the entry, time is zero - when(clock.timestampNano()).thenReturn((long) 0); - cache.load("k1"); - verify(loader, times(1)).load("k1"); - assertTrue(cache.size() == 1); - - // on load, time is past TTL and grace period - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); - assertThrows(IllegalStateException.class, () -> cache.load("k1")); - - verify(loader, times(2)).load("k1"); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 0); - } - - @Test - public void testLoadWithFunction() { - final String loadedValue = "loaded value"; - final String functionValue = "function value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - final Function function = spy(Function.class); - when(function.apply(any())).thenReturn(functionValue); - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue).thenThrow(new IllegalStateException("This loader is mocked to throw a failure.")); - MsClock clock = mock(MsClock.class); - when(clock.timestampNano()).thenReturn((long) 0); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - String result = cache.load("k1", function); - verify(function, times(1)).apply("k1"); - assertTrue(cache.size() == 1); - assertEquals(functionValue, result); - - // to verify result is in the cache, load one more time - // and expect the loader to not be called - String cachedValue = cache.load("k1"); - verifyNoMoreInteractions(function); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(functionValue, cachedValue); - } - - @Test - public void testClear() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - - assertTrue(cache.size() == 0); - cache.load("k1"); - cache.load("k2"); - assertTrue(cache.size() == 2); - - cache.clear(); - assertTrue(cache.size() == 0); - } - - @Test - public void testPut() { - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - MsClock clock = mock(MsClock.class); - when(clock.timestampNano()).thenReturn((long) 0); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - String oldValue = cache.put("k1", "v1"); - assertNull(oldValue); - assertTrue(cache.size() == 1); - - String oldValue2 = cache.put("k1", "v11"); - assertEquals("v1", oldValue2); - assertTrue(cache.size() == 1); - } - - @Test - public void testExpiredPut() { - final long ttlInMillis = 1000; - final int maxSize = 3; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - MsClock clock = mock(MsClock.class); - when(clock.timestampNano()).thenReturn((long) 0); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - // First put is at time 0 - String oldValue = cache.put("k1", "v1"); - assertNull(oldValue); - assertTrue(cache.size() == 1); - - // Second put is at time past TTL and grace period - when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); - String oldValue2 = cache.put("k1", "v11"); - assertNull(oldValue2); - assertTrue(cache.size() == 1); - } - - @Test - public void testPutPastMaxSize() { - final String loadedValue = "loaded value"; - final long ttlInMillis = 1000; - final int maxSize = 1; - TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); - when(loader.load(any())).thenReturn(loadedValue); - MsClock clock = mock(MsClock.class); - when(clock.timestampNano()).thenReturn((long) 0); - - final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); - cache.clock = clock; - - assertEquals(0, cache.size()); - assertEquals(maxSize, cache.getMaxSize()); - - cache.put("k1", "v1"); - assertTrue(cache.size() == 1); - - cache.put("k2", "v2"); - assertTrue(cache.size() == 1); - - // to verify put value is in the cache, load - // and expect the loader to not be called - String cachedValue = cache.load("k2"); - verifyNoMoreInteractions(loader); - assertTrue(cache.size() == 1); - assertEquals(cachedValue, "v2"); - } + private static final long TTL_GRACE_IN_NANO = TimeUnit.MILLISECONDS.toNanos(500); + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testInvalidSize() { + final TTLCache cache = new TTLCache(0, 1000, mock(TTLCache.EntryLoader.class)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testInvalidTTL() { + final TTLCache cache = new TTLCache(3, 0, mock(TTLCache.EntryLoader.class)); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullLoader() { + final TTLCache cache = new TTLCache(3, 1000, null); + } + + @Test + public void testConstructor() { + final TTLCache cache = + new TTLCache(1000, 1000, mock(TTLCache.EntryLoader.class)); + assertEquals(0, cache.size()); + assertEquals(1000, cache.getMaxSize()); + } + + @Test + public void testLoadPastMaxSize() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 1; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + MsClock clock = mock(MsClock.class); + when(clock.timestampNano()).thenReturn((long) 0); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + cache.load("k1"); + verify(loader, times(1)).load("k1"); + assertTrue(cache.size() == 1); + + String result = cache.load("k2"); + verify(loader, times(1)).load("k2"); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, result); + + // to verify result is in the cache, load one more time + // and expect the loader to not be called + String cachedValue = cache.load("k2"); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, cachedValue); + } + + @Test + public void testLoadNoExistingEntry() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + MsClock clock = mock(MsClock.class); + when(clock.timestampNano()).thenReturn((long) 0); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + String result = cache.load("k1"); + verify(loader, times(1)).load("k1"); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, result); + + // to verify result is in the cache, load one more time + // and expect the loader to not be called + String cachedValue = cache.load("k1"); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, cachedValue); + } + + @Test + public void testLoadNotExpired() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + MsClock clock = mock(MsClock.class); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + // when first creating the entry, time is 0 + when(clock.timestampNano()).thenReturn((long) 0); + cache.load("k1"); + assertTrue(cache.size() == 1); + verify(loader, times(1)).load("k1"); + + // on load, time is within TTL + when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis)); + String result = cache.load("k1"); + + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, result); + } + + @Test + public void testLoadInGrace() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + MsClock clock = mock(MsClock.class); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + // when first creating the entry, time is zero + when(clock.timestampNano()).thenReturn((long) 0); + cache.load("k1"); + assertTrue(cache.size() == 1); + verify(loader, times(1)).load("k1"); + + // on load, time is past TTL but within the grace period + when(clock.timestampNano()).thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + 1); + String result = cache.load("k1"); + + // Because this is tested in a single thread, + // this is expected to obtain the lock and load the new value + verify(loader, times(2)).load("k1"); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, result); + } + + @Test + public void testLoadExpired() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + MsClock clock = mock(MsClock.class); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + // when first creating the entry, time is zero + when(clock.timestampNano()).thenReturn((long) 0); + cache.load("k1"); + assertTrue(cache.size() == 1); + verify(loader, times(1)).load("k1"); + + // on load, time is past TTL and grace period + when(clock.timestampNano()) + .thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); + String result = cache.load("k1"); + + verify(loader, times(2)).load("k1"); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(loadedValue, result); + } + + @Test + public void testLoadExpiredEviction() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())) + .thenReturn(loadedValue) + .thenThrow(new IllegalStateException("This loader is mocked to throw a failure.")); + MsClock clock = mock(MsClock.class); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + // when first creating the entry, time is zero + when(clock.timestampNano()).thenReturn((long) 0); + cache.load("k1"); + verify(loader, times(1)).load("k1"); + assertTrue(cache.size() == 1); + + // on load, time is past TTL and grace period + when(clock.timestampNano()) + .thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); + assertThrows(IllegalStateException.class, () -> cache.load("k1")); + + verify(loader, times(2)).load("k1"); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 0); + } + + @Test + public void testLoadWithFunction() { + final String loadedValue = "loaded value"; + final String functionValue = "function value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + final Function function = spy(Function.class); + when(function.apply(any())).thenReturn(functionValue); + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())) + .thenReturn(loadedValue) + .thenThrow(new IllegalStateException("This loader is mocked to throw a failure.")); + MsClock clock = mock(MsClock.class); + when(clock.timestampNano()).thenReturn((long) 0); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + String result = cache.load("k1", function); + verify(function, times(1)).apply("k1"); + assertTrue(cache.size() == 1); + assertEquals(functionValue, result); + + // to verify result is in the cache, load one more time + // and expect the loader to not be called + String cachedValue = cache.load("k1"); + verifyNoMoreInteractions(function); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(functionValue, cachedValue); + } + + @Test + public void testClear() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + + assertTrue(cache.size() == 0); + cache.load("k1"); + cache.load("k2"); + assertTrue(cache.size() == 2); + + cache.clear(); + assertTrue(cache.size() == 0); + } + + @Test + public void testPut() { + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + MsClock clock = mock(MsClock.class); + when(clock.timestampNano()).thenReturn((long) 0); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + String oldValue = cache.put("k1", "v1"); + assertNull(oldValue); + assertTrue(cache.size() == 1); + + String oldValue2 = cache.put("k1", "v11"); + assertEquals("v1", oldValue2); + assertTrue(cache.size() == 1); + } + + @Test + public void testExpiredPut() { + final long ttlInMillis = 1000; + final int maxSize = 3; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + MsClock clock = mock(MsClock.class); + when(clock.timestampNano()).thenReturn((long) 0); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + // First put is at time 0 + String oldValue = cache.put("k1", "v1"); + assertNull(oldValue); + assertTrue(cache.size() == 1); + + // Second put is at time past TTL and grace period + when(clock.timestampNano()) + .thenReturn(TimeUnit.MILLISECONDS.toNanos(ttlInMillis) + TTL_GRACE_IN_NANO + 1); + String oldValue2 = cache.put("k1", "v11"); + assertNull(oldValue2); + assertTrue(cache.size() == 1); + } + + @Test + public void testPutPastMaxSize() { + final String loadedValue = "loaded value"; + final long ttlInMillis = 1000; + final int maxSize = 1; + TTLCache.EntryLoader loader = spy(TTLCache.EntryLoader.class); + when(loader.load(any())).thenReturn(loadedValue); + MsClock clock = mock(MsClock.class); + when(clock.timestampNano()).thenReturn((long) 0); + + final TTLCache cache = new TTLCache(maxSize, ttlInMillis, loader); + cache.clock = clock; + + assertEquals(0, cache.size()); + assertEquals(maxSize, cache.getMaxSize()); + + cache.put("k1", "v1"); + assertTrue(cache.size() == 1); + + cache.put("k2", "v2"); + assertTrue(cache.size() == 1); + + // to verify put value is in the cache, load + // and expect the loader to not be called + String cachedValue = cache.load("k2"); + verifyNoMoreInteractions(loader); + assertTrue(cache.size() == 1); + assertEquals(cachedValue, "v2"); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteArrayTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteArrayTestClass.java index 032717cd..fbea5383 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteArrayTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteArrayTestClass.java @@ -15,81 +15,68 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; - import java.util.Set; -/** - * Test domain class with byte[] attribute, byte[] set and a string key - */ +/** Test domain class with byte[] attribute, byte[] set and a string key */ @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public class BinaryAttributeByteArrayTestClass { - private String key; - private byte[] binaryAttribute; - private Set binarySetAttribute; - - @DynamoDBHashKey(attributeName = "key") - public String getKey() { - return key; - } + private String key; + private byte[] binaryAttribute; + private Set binarySetAttribute; - public void setKey(String key) { - this.key = key; - } + @DynamoDBHashKey(attributeName = "key") + public String getKey() { + return key; + } - @DynamoDBAttribute(attributeName = "binaryAttribute") - public byte[] getBinaryAttribute() { - return binaryAttribute; - } + public void setKey(String key) { + this.key = key; + } - public void setBinaryAttribute(byte[] binaryAttribute) { - this.binaryAttribute = binaryAttribute; - } + @DynamoDBAttribute(attributeName = "binaryAttribute") + public byte[] getBinaryAttribute() { + return binaryAttribute; + } - @DynamoDBAttribute(attributeName = "binarySetAttribute") - public Set getBinarySetAttribute() { - return binarySetAttribute; - } + public void setBinaryAttribute(byte[] binaryAttribute) { + this.binaryAttribute = binaryAttribute; + } - public void setBinarySetAttribute(Set binarySetAttribute) { - this.binarySetAttribute = binarySetAttribute; - } + @DynamoDBAttribute(attributeName = "binarySetAttribute") + public Set getBinarySetAttribute() { + return binarySetAttribute; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((binaryAttribute == null) ? 0 : binaryAttribute.hashCode()); - result = prime * result + ((binarySetAttribute == null) ? 0 : binarySetAttribute.hashCode()); - return result; - } + public void setBinarySetAttribute(Set binarySetAttribute) { + this.binarySetAttribute = binarySetAttribute; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BinaryAttributeByteArrayTestClass other = (BinaryAttributeByteArrayTestClass) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (binaryAttribute == null) { - if (other.binaryAttribute != null) - return false; - } else if (!binaryAttribute.equals(other.binaryAttribute)) - return false; - if (binarySetAttribute == null) { - if (other.binarySetAttribute != null) - return false; - } else if (!binarySetAttribute.equals(other.binarySetAttribute)) - return false; - return true; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((binaryAttribute == null) ? 0 : binaryAttribute.hashCode()); + result = prime * result + ((binarySetAttribute == null) ? 0 : binarySetAttribute.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + BinaryAttributeByteArrayTestClass other = (BinaryAttributeByteArrayTestClass) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (binaryAttribute == null) { + if (other.binaryAttribute != null) return false; + } else if (!binaryAttribute.equals(other.binaryAttribute)) return false; + if (binarySetAttribute == null) { + if (other.binarySetAttribute != null) return false; + } else if (!binarySetAttribute.equals(other.binarySetAttribute)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteBufferTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteBufferTestClass.java index 5ad9f32d..e5ce1c8c 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteBufferTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/BinaryAttributeByteBufferTestClass.java @@ -15,82 +15,69 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; - import java.nio.ByteBuffer; import java.util.Set; -/** - * Test domain class with byteBuffer attribute, byteBuffer set and a string key - */ +/** Test domain class with byteBuffer attribute, byteBuffer set and a string key */ @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public class BinaryAttributeByteBufferTestClass { - private String key; - private ByteBuffer binaryAttribute; - private Set binarySetAttribute; - - @DynamoDBHashKey(attributeName = "key") - public String getKey() { - return key; - } + private String key; + private ByteBuffer binaryAttribute; + private Set binarySetAttribute; - public void setKey(String key) { - this.key = key; - } + @DynamoDBHashKey(attributeName = "key") + public String getKey() { + return key; + } - @DynamoDBAttribute(attributeName = "binaryAttribute") - public ByteBuffer getBinaryAttribute() { - return binaryAttribute; - } + public void setKey(String key) { + this.key = key; + } - public void setBinaryAttribute(ByteBuffer binaryAttribute) { - this.binaryAttribute = binaryAttribute; - } + @DynamoDBAttribute(attributeName = "binaryAttribute") + public ByteBuffer getBinaryAttribute() { + return binaryAttribute; + } - @DynamoDBAttribute(attributeName = "binarySetAttribute") - public Set getBinarySetAttribute() { - return binarySetAttribute; - } + public void setBinaryAttribute(ByteBuffer binaryAttribute) { + this.binaryAttribute = binaryAttribute; + } - public void setBinarySetAttribute(Set binarySetAttribute) { - this.binarySetAttribute = binarySetAttribute; - } + @DynamoDBAttribute(attributeName = "binarySetAttribute") + public Set getBinarySetAttribute() { + return binarySetAttribute; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((binaryAttribute == null) ? 0 : binaryAttribute.hashCode()); - result = prime * result + ((binarySetAttribute == null) ? 0 : binarySetAttribute.hashCode()); - return result; - } + public void setBinarySetAttribute(Set binarySetAttribute) { + this.binarySetAttribute = binarySetAttribute; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BinaryAttributeByteBufferTestClass other = (BinaryAttributeByteBufferTestClass) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (binaryAttribute == null) { - if (other.binaryAttribute != null) - return false; - } else if (!binaryAttribute.equals(other.binaryAttribute)) - return false; - if (binarySetAttribute == null) { - if (other.binarySetAttribute != null) - return false; - } else if (!binarySetAttribute.equals(other.binarySetAttribute)) - return false; - return true; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((binaryAttribute == null) ? 0 : binaryAttribute.hashCode()); + result = prime * result + ((binarySetAttribute == null) ? 0 : binarySetAttribute.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + BinaryAttributeByteBufferTestClass other = (BinaryAttributeByteBufferTestClass) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (binaryAttribute == null) { + if (other.binaryAttribute != null) return false; + } else if (!binaryAttribute.equals(other.binaryAttribute)) return false; + if (binarySetAttribute == null) { + if (other.binarySetAttribute != null) return false; + } else if (!binarySetAttribute.equals(other.binarySetAttribute)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/CrossSDKVerificationTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/CrossSDKVerificationTestClass.java index c5693bb3..d09250b1 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/CrossSDKVerificationTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/CrossSDKVerificationTestClass.java @@ -17,421 +17,366 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBVersionAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotEncrypt; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.Calendar; import java.util.Date; import java.util.Set; -/** - * Exhaustive exercise of DynamoDB domain mapping, exercising every supported - * data type. - */ +/** Exhaustive exercise of DynamoDB domain mapping, exercising every supported data type. */ @DynamoDBTable(tableName = "aws-xsdk-crypto") public class CrossSDKVerificationTestClass { - private String key; - private String rangeKey; - private Long version; - private String lastUpdater; - - private Integer integerAttribute; - private Long longAttribute; - private Double doubleAttribute; - private Float floatAttribute; - private BigDecimal bigDecimalAttribute; - private BigInteger bigIntegerAttribute; - private Byte byteAttribute; - private Date dateAttribute; - private Calendar calendarAttribute; - private Boolean booleanAttribute; - - private Set stringSetAttribute; - private Set integerSetAttribute; - private Set doubleSetAttribute; - private Set floatSetAttribute; - private Set bigDecimalSetAttribute; - private Set bigIntegerSetAttribute; - private Set longSetAttribute; - private Set byteSetAttribute; - private Set dateSetAttribute; - private Set calendarSetAttribute; - - // these are kind of pointless, but here for completeness - private Set booleanSetAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @DoNotEncrypt - @DynamoDBVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - public String getLastUpdater() { - return lastUpdater; - } - - public void setLastUpdater(String lastUpdater) { - this.lastUpdater = lastUpdater; - } - - public Integer getIntegerAttribute() { - return integerAttribute; - } - - public void setIntegerAttribute(Integer integerAttribute) { - this.integerAttribute = integerAttribute; - } - - public Long getLongAttribute() { - return longAttribute; - } - - public void setLongAttribute(Long longAttribute) { - this.longAttribute = longAttribute; - } - - public Double getDoubleAttribute() { - return doubleAttribute; - } - - public void setDoubleAttribute(Double doubleAttribute) { - this.doubleAttribute = doubleAttribute; - } - - public Float getFloatAttribute() { - return floatAttribute; - } - - public void setFloatAttribute(Float floatAttribute) { - this.floatAttribute = floatAttribute; - } - - public BigDecimal getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - public BigInteger getBigIntegerAttribute() { - return bigIntegerAttribute; - } - - public void setBigIntegerAttribute(BigInteger bigIntegerAttribute) { - this.bigIntegerAttribute = bigIntegerAttribute; - } - - public Byte getByteAttribute() { - return byteAttribute; - } - - public void setByteAttribute(Byte byteAttribute) { - this.byteAttribute = byteAttribute; - } - - public Date getDateAttribute() { - return dateAttribute; - } - - public void setDateAttribute(Date dateAttribute) { - this.dateAttribute = dateAttribute; - } + private String key; + private String rangeKey; + private Long version; + private String lastUpdater; + + private Integer integerAttribute; + private Long longAttribute; + private Double doubleAttribute; + private Float floatAttribute; + private BigDecimal bigDecimalAttribute; + private BigInteger bigIntegerAttribute; + private Byte byteAttribute; + private Date dateAttribute; + private Calendar calendarAttribute; + private Boolean booleanAttribute; + + private Set stringSetAttribute; + private Set integerSetAttribute; + private Set doubleSetAttribute; + private Set floatSetAttribute; + private Set bigDecimalSetAttribute; + private Set bigIntegerSetAttribute; + private Set longSetAttribute; + private Set byteSetAttribute; + private Set dateSetAttribute; + private Set calendarSetAttribute; + + // these are kind of pointless, but here for completeness + private Set booleanSetAttribute; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + @DoNotEncrypt + @DynamoDBVersionAttribute + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getLastUpdater() { + return lastUpdater; + } + + public void setLastUpdater(String lastUpdater) { + this.lastUpdater = lastUpdater; + } + + public Integer getIntegerAttribute() { + return integerAttribute; + } + + public void setIntegerAttribute(Integer integerAttribute) { + this.integerAttribute = integerAttribute; + } + + public Long getLongAttribute() { + return longAttribute; + } + + public void setLongAttribute(Long longAttribute) { + this.longAttribute = longAttribute; + } + + public Double getDoubleAttribute() { + return doubleAttribute; + } + + public void setDoubleAttribute(Double doubleAttribute) { + this.doubleAttribute = doubleAttribute; + } + + public Float getFloatAttribute() { + return floatAttribute; + } + + public void setFloatAttribute(Float floatAttribute) { + this.floatAttribute = floatAttribute; + } + + public BigDecimal getBigDecimalAttribute() { + return bigDecimalAttribute; + } + + public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { + this.bigDecimalAttribute = bigDecimalAttribute; + } + + public BigInteger getBigIntegerAttribute() { + return bigIntegerAttribute; + } + + public void setBigIntegerAttribute(BigInteger bigIntegerAttribute) { + this.bigIntegerAttribute = bigIntegerAttribute; + } + + public Byte getByteAttribute() { + return byteAttribute; + } + + public void setByteAttribute(Byte byteAttribute) { + this.byteAttribute = byteAttribute; + } + + public Date getDateAttribute() { + return dateAttribute; + } - public Calendar getCalendarAttribute() { - return calendarAttribute; - } + public void setDateAttribute(Date dateAttribute) { + this.dateAttribute = dateAttribute; + } - public void setCalendarAttribute(Calendar calendarAttribute) { - this.calendarAttribute = calendarAttribute; - } - - public Boolean getBooleanAttribute() { - return booleanAttribute; - } - - public void setBooleanAttribute(Boolean booleanAttribute) { - this.booleanAttribute = booleanAttribute; - } - - public Set getIntegerSetAttribute() { - return integerSetAttribute; - } - - public void setIntegerSetAttribute(Set integerSetAttribute) { - this.integerSetAttribute = integerSetAttribute; - } - - public Set getDoubleSetAttribute() { - return doubleSetAttribute; - } - - public void setDoubleSetAttribute(Set doubleSetAttribute) { - this.doubleSetAttribute = doubleSetAttribute; - } - - public Set getFloatSetAttribute() { - return floatSetAttribute; - } - - public void setFloatSetAttribute(Set floatSetAttribute) { - this.floatSetAttribute = floatSetAttribute; - } - - public Set getBigDecimalSetAttribute() { - return bigDecimalSetAttribute; - } - - public void setBigDecimalSetAttribute(Set bigDecimalSetAttribute) { - this.bigDecimalSetAttribute = bigDecimalSetAttribute; - } - - public Set getBigIntegerSetAttribute() { - return bigIntegerSetAttribute; - } - - public void setBigIntegerSetAttribute(Set bigIntegerSetAttribute) { - this.bigIntegerSetAttribute = bigIntegerSetAttribute; - } - - public Set getLongSetAttribute() { - return longSetAttribute; - } - - public void setLongSetAttribute(Set longSetAttribute) { - this.longSetAttribute = longSetAttribute; - } - - public Set getByteSetAttribute() { - return byteSetAttribute; - } - - public void setByteSetAttribute(Set byteSetAttribute) { - this.byteSetAttribute = byteSetAttribute; - } - - public Set getDateSetAttribute() { - return dateSetAttribute; - } - - public void setDateSetAttribute(Set dateSetAttribute) { - this.dateSetAttribute = dateSetAttribute; - } - - public Set getCalendarSetAttribute() { - return calendarSetAttribute; - } - - public void setCalendarSetAttribute(Set calendarSetAttribute) { - this.calendarSetAttribute = calendarSetAttribute; - } - - public Set getBooleanSetAttribute() { - return booleanSetAttribute; - } - - public void setBooleanSetAttribute(Set booleanSetAttribute) { - this.booleanSetAttribute = booleanSetAttribute; - } - - public Set getStringSetAttribute() { - return stringSetAttribute; - } - - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((bigDecimalSetAttribute == null) ? 0 : bigDecimalSetAttribute.hashCode()); - result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); - result = prime * result + ((bigIntegerSetAttribute == null) ? 0 : bigIntegerSetAttribute.hashCode()); - result = prime * result + ((booleanAttribute == null) ? 0 : booleanAttribute.hashCode()); - result = prime * result + ((booleanSetAttribute == null) ? 0 : booleanSetAttribute.hashCode()); - result = prime * result + ((byteAttribute == null) ? 0 : byteAttribute.hashCode()); - result = prime * result + ((byteSetAttribute == null) ? 0 : byteSetAttribute.hashCode()); - result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); - result = prime * result + ((calendarSetAttribute == null) ? 0 : calendarSetAttribute.hashCode()); - result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); - result = prime * result + ((dateSetAttribute == null) ? 0 : dateSetAttribute.hashCode()); - result = prime * result + ((doubleAttribute == null) ? 0 : doubleAttribute.hashCode()); - result = prime * result + ((doubleSetAttribute == null) ? 0 : doubleSetAttribute.hashCode()); - result = prime * result + ((floatAttribute == null) ? 0 : floatAttribute.hashCode()); - result = prime * result + ((floatSetAttribute == null) ? 0 : floatSetAttribute.hashCode()); - result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); - result = prime * result + ((integerSetAttribute == null) ? 0 : integerSetAttribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((lastUpdater == null) ? 0 : lastUpdater.hashCode()); - result = prime * result + ((longAttribute == null) ? 0 : longAttribute.hashCode()); - result = prime * result + ((longSetAttribute == null) ? 0 : longSetAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CrossSDKVerificationTestClass other = (CrossSDKVerificationTestClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) - return false; - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) - return false; - if (bigDecimalSetAttribute == null) { - if (other.bigDecimalSetAttribute != null) - return false; - } else if (!bigDecimalSetAttribute.equals(other.bigDecimalSetAttribute)) - return false; - if (bigIntegerAttribute == null) { - if (other.bigIntegerAttribute != null) - return false; - } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) - return false; - if (bigIntegerSetAttribute == null) { - if (other.bigIntegerSetAttribute != null) - return false; - } else if (!bigIntegerSetAttribute.equals(other.bigIntegerSetAttribute)) - return false; - if (booleanAttribute == null) { - if (other.booleanAttribute != null) - return false; - } else if (!booleanAttribute.equals(other.booleanAttribute)) - return false; - if (booleanSetAttribute == null) { - if (other.booleanSetAttribute != null) - return false; - } else if (!booleanSetAttribute.equals(other.booleanSetAttribute)) - return false; - if (byteAttribute == null) { - if (other.byteAttribute != null) - return false; - } else if (!byteAttribute.equals(other.byteAttribute)) - return false; - if (byteSetAttribute == null) { - if (other.byteSetAttribute != null) - return false; - } else if (!byteSetAttribute.equals(other.byteSetAttribute)) - return false; - if (calendarAttribute == null) { - if (other.calendarAttribute != null) - return false; - } else if (!calendarAttribute.equals(other.calendarAttribute)) - return false; - if (calendarSetAttribute == null) { - if (other.calendarSetAttribute != null) - return false; - } else if (!calendarSetAttribute.equals(other.calendarSetAttribute)) - return false; - if (dateAttribute == null) { - if (other.dateAttribute != null) - return false; - } else if (!dateAttribute.equals(other.dateAttribute)) - return false; - if (dateSetAttribute == null) { - if (other.dateSetAttribute != null) - return false; - } else if (!dateSetAttribute.equals(other.dateSetAttribute)) - return false; - if (doubleAttribute == null) { - if (other.doubleAttribute != null) - return false; - } else if (!doubleAttribute.equals(other.doubleAttribute)) - return false; - if (doubleSetAttribute == null) { - if (other.doubleSetAttribute != null) - return false; - } else if (!doubleSetAttribute.equals(other.doubleSetAttribute)) - return false; - if (floatAttribute == null) { - if (other.floatAttribute != null) - return false; - } else if (!floatAttribute.equals(other.floatAttribute)) - return false; - if (floatSetAttribute == null) { - if (other.floatSetAttribute != null) - return false; - } else if (!floatSetAttribute.equals(other.floatSetAttribute)) - return false; - if (integerAttribute == null) { - if (other.integerAttribute != null) - return false; - } else if (!integerAttribute.equals(other.integerAttribute)) - return false; - if (integerSetAttribute == null) { - if (other.integerSetAttribute != null) - return false; - } else if (!integerSetAttribute.equals(other.integerSetAttribute)) - return false; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (lastUpdater == null) { - if (other.lastUpdater != null) - return false; - } else if (!lastUpdater.equals(other.lastUpdater)) - return false; - if (longAttribute == null) { - if (other.longAttribute != null) - return false; - } else if (!longAttribute.equals(other.longAttribute)) - return false; - if (longSetAttribute == null) { - if (other.longSetAttribute != null) - return false; - } else if (!longSetAttribute.equals(other.longSetAttribute)) - return false; - if (rangeKey == null) { - if (other.rangeKey != null) - return false; - } else if (!rangeKey.equals(other.rangeKey)) - return false; - if (stringSetAttribute == null) { - if (other.stringSetAttribute != null) - return false; - } else if (!stringSetAttribute.equals(other.stringSetAttribute)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - return true; - } + public Calendar getCalendarAttribute() { + return calendarAttribute; + } + public void setCalendarAttribute(Calendar calendarAttribute) { + this.calendarAttribute = calendarAttribute; + } + + public Boolean getBooleanAttribute() { + return booleanAttribute; + } + + public void setBooleanAttribute(Boolean booleanAttribute) { + this.booleanAttribute = booleanAttribute; + } + + public Set getIntegerSetAttribute() { + return integerSetAttribute; + } + + public void setIntegerSetAttribute(Set integerSetAttribute) { + this.integerSetAttribute = integerSetAttribute; + } + + public Set getDoubleSetAttribute() { + return doubleSetAttribute; + } + + public void setDoubleSetAttribute(Set doubleSetAttribute) { + this.doubleSetAttribute = doubleSetAttribute; + } + + public Set getFloatSetAttribute() { + return floatSetAttribute; + } + + public void setFloatSetAttribute(Set floatSetAttribute) { + this.floatSetAttribute = floatSetAttribute; + } + + public Set getBigDecimalSetAttribute() { + return bigDecimalSetAttribute; + } + + public void setBigDecimalSetAttribute(Set bigDecimalSetAttribute) { + this.bigDecimalSetAttribute = bigDecimalSetAttribute; + } + + public Set getBigIntegerSetAttribute() { + return bigIntegerSetAttribute; + } + + public void setBigIntegerSetAttribute(Set bigIntegerSetAttribute) { + this.bigIntegerSetAttribute = bigIntegerSetAttribute; + } + + public Set getLongSetAttribute() { + return longSetAttribute; + } + + public void setLongSetAttribute(Set longSetAttribute) { + this.longSetAttribute = longSetAttribute; + } + + public Set getByteSetAttribute() { + return byteSetAttribute; + } + + public void setByteSetAttribute(Set byteSetAttribute) { + this.byteSetAttribute = byteSetAttribute; + } + + public Set getDateSetAttribute() { + return dateSetAttribute; + } + + public void setDateSetAttribute(Set dateSetAttribute) { + this.dateSetAttribute = dateSetAttribute; + } + + public Set getCalendarSetAttribute() { + return calendarSetAttribute; + } + + public void setCalendarSetAttribute(Set calendarSetAttribute) { + this.calendarSetAttribute = calendarSetAttribute; + } + + public Set getBooleanSetAttribute() { + return booleanSetAttribute; + } + + public void setBooleanSetAttribute(Set booleanSetAttribute) { + this.booleanSetAttribute = booleanSetAttribute; + } + + public Set getStringSetAttribute() { + return stringSetAttribute; + } + + public void setStringSetAttribute(Set stringSetAttribute) { + this.stringSetAttribute = stringSetAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); + result = + prime * result + ((bigDecimalSetAttribute == null) ? 0 : bigDecimalSetAttribute.hashCode()); + result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); + result = + prime * result + ((bigIntegerSetAttribute == null) ? 0 : bigIntegerSetAttribute.hashCode()); + result = prime * result + ((booleanAttribute == null) ? 0 : booleanAttribute.hashCode()); + result = prime * result + ((booleanSetAttribute == null) ? 0 : booleanSetAttribute.hashCode()); + result = prime * result + ((byteAttribute == null) ? 0 : byteAttribute.hashCode()); + result = prime * result + ((byteSetAttribute == null) ? 0 : byteSetAttribute.hashCode()); + result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); + result = + prime * result + ((calendarSetAttribute == null) ? 0 : calendarSetAttribute.hashCode()); + result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); + result = prime * result + ((dateSetAttribute == null) ? 0 : dateSetAttribute.hashCode()); + result = prime * result + ((doubleAttribute == null) ? 0 : doubleAttribute.hashCode()); + result = prime * result + ((doubleSetAttribute == null) ? 0 : doubleSetAttribute.hashCode()); + result = prime * result + ((floatAttribute == null) ? 0 : floatAttribute.hashCode()); + result = prime * result + ((floatSetAttribute == null) ? 0 : floatSetAttribute.hashCode()); + result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); + result = prime * result + ((integerSetAttribute == null) ? 0 : integerSetAttribute.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((lastUpdater == null) ? 0 : lastUpdater.hashCode()); + result = prime * result + ((longAttribute == null) ? 0 : longAttribute.hashCode()); + result = prime * result + ((longSetAttribute == null) ? 0 : longSetAttribute.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + CrossSDKVerificationTestClass other = (CrossSDKVerificationTestClass) obj; + if (bigDecimalAttribute == null) { + if (other.bigDecimalAttribute != null) return false; + } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) return false; + if (bigDecimalSetAttribute == null) { + if (other.bigDecimalSetAttribute != null) return false; + } else if (!bigDecimalSetAttribute.equals(other.bigDecimalSetAttribute)) return false; + if (bigIntegerAttribute == null) { + if (other.bigIntegerAttribute != null) return false; + } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) return false; + if (bigIntegerSetAttribute == null) { + if (other.bigIntegerSetAttribute != null) return false; + } else if (!bigIntegerSetAttribute.equals(other.bigIntegerSetAttribute)) return false; + if (booleanAttribute == null) { + if (other.booleanAttribute != null) return false; + } else if (!booleanAttribute.equals(other.booleanAttribute)) return false; + if (booleanSetAttribute == null) { + if (other.booleanSetAttribute != null) return false; + } else if (!booleanSetAttribute.equals(other.booleanSetAttribute)) return false; + if (byteAttribute == null) { + if (other.byteAttribute != null) return false; + } else if (!byteAttribute.equals(other.byteAttribute)) return false; + if (byteSetAttribute == null) { + if (other.byteSetAttribute != null) return false; + } else if (!byteSetAttribute.equals(other.byteSetAttribute)) return false; + if (calendarAttribute == null) { + if (other.calendarAttribute != null) return false; + } else if (!calendarAttribute.equals(other.calendarAttribute)) return false; + if (calendarSetAttribute == null) { + if (other.calendarSetAttribute != null) return false; + } else if (!calendarSetAttribute.equals(other.calendarSetAttribute)) return false; + if (dateAttribute == null) { + if (other.dateAttribute != null) return false; + } else if (!dateAttribute.equals(other.dateAttribute)) return false; + if (dateSetAttribute == null) { + if (other.dateSetAttribute != null) return false; + } else if (!dateSetAttribute.equals(other.dateSetAttribute)) return false; + if (doubleAttribute == null) { + if (other.doubleAttribute != null) return false; + } else if (!doubleAttribute.equals(other.doubleAttribute)) return false; + if (doubleSetAttribute == null) { + if (other.doubleSetAttribute != null) return false; + } else if (!doubleSetAttribute.equals(other.doubleSetAttribute)) return false; + if (floatAttribute == null) { + if (other.floatAttribute != null) return false; + } else if (!floatAttribute.equals(other.floatAttribute)) return false; + if (floatSetAttribute == null) { + if (other.floatSetAttribute != null) return false; + } else if (!floatSetAttribute.equals(other.floatSetAttribute)) return false; + if (integerAttribute == null) { + if (other.integerAttribute != null) return false; + } else if (!integerAttribute.equals(other.integerAttribute)) return false; + if (integerSetAttribute == null) { + if (other.integerSetAttribute != null) return false; + } else if (!integerSetAttribute.equals(other.integerSetAttribute)) return false; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (lastUpdater == null) { + if (other.lastUpdater != null) return false; + } else if (!lastUpdater.equals(other.lastUpdater)) return false; + if (longAttribute == null) { + if (other.longAttribute != null) return false; + } else if (!longAttribute.equals(other.longAttribute)) return false; + if (longSetAttribute == null) { + if (other.longSetAttribute != null) return false; + } else if (!longSetAttribute.equals(other.longSetAttribute)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + if (stringSetAttribute == null) { + if (other.stringSetAttribute != null) return false; + } else if (!stringSetAttribute.equals(other.stringSetAttribute)) return false; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/IndexRangeKeyTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/IndexRangeKeyTestClass.java index ac63f6b0..b9676424 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/IndexRangeKeyTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/IndexRangeKeyTestClass.java @@ -20,163 +20,154 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBVersionAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotEncrypt; -/** - * Comprehensive domain class - */ +/** Comprehensive domain class */ @DynamoDBTable(tableName = "aws-java-sdk-index-range-test-crypto") public class IndexRangeKeyTestClass { - private long key; - private double rangeKey; - private Double indexFooRangeKey; - private Double indexBarRangeKey; - private Double multipleIndexRangeKey; - private Long version; - - private String fooAttribute; - private String barAttribute; - - @DynamoDBHashKey - public long getKey() { - return key; - } - - public void setKey(long key) { - this.key = key; - } - - @DynamoDBRangeKey - public double getRangeKey() { - return rangeKey; - } - - public void setRangeKey(double rangeKey) { - this.rangeKey = rangeKey; - } - - @DoNotEncrypt - @DynamoDBIndexRangeKey( - localSecondaryIndexName = "index_foo", - attributeName = "indexFooRangeKey" - ) - public Double getIndexFooRangeKeyWithFakeName() { - return indexFooRangeKey; - } - - public void setIndexFooRangeKeyWithFakeName(Double indexFooRangeKey) { - this.indexFooRangeKey = indexFooRangeKey; - } - - @DoNotEncrypt - @DynamoDBIndexRangeKey( - localSecondaryIndexName = "index_bar" - ) - public Double getIndexBarRangeKey() { - return indexBarRangeKey; - } - - public void setIndexBarRangeKey(Double indexBarRangeKey) { - this.indexBarRangeKey = indexBarRangeKey; - } - - @DoNotEncrypt - @DynamoDBIndexRangeKey( - localSecondaryIndexNames = {"index_foo_copy", "index_bar_copy"} - ) - public Double getMultipleIndexRangeKey() { - return multipleIndexRangeKey; - } - - public void setMultipleIndexRangeKey(Double multipleIndexRangeKey) { - this.multipleIndexRangeKey = multipleIndexRangeKey; - } - - @DynamoDBAttribute - public String getFooAttribute() { - return fooAttribute; - } - - public void setFooAttribute(String fooAttribute) { - this.fooAttribute = fooAttribute; - } - - @DynamoDBAttribute - public String getBarAttribute() { - return barAttribute; - } - - public void setBarAttribute(String barAttribute) { - this.barAttribute = barAttribute; - } - - @DoNotEncrypt - @DynamoDBVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((fooAttribute == null) ? 0 : fooAttribute.hashCode()); - result = prime * result + ((barAttribute == null) ? 0 : barAttribute.hashCode()); - result = prime * result + (int) (key ^ (key >>> 32)); - long temp; - temp = Double.doubleToLongBits(rangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(indexFooRangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(indexBarRangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - IndexRangeKeyTestClass other = (IndexRangeKeyTestClass) obj; - if (fooAttribute == null) { - if (other.fooAttribute != null) - return false; - } else if (!fooAttribute.equals(other.fooAttribute)) - return false; - if (barAttribute == null) { - if (other.barAttribute != null) - return false; - } else if (!barAttribute.equals(other.barAttribute)) - return false; - if (key != other.key) - return false; - if (Double.doubleToLongBits(rangeKey) != Double.doubleToLongBits(other.rangeKey)) - return false; - if (Double.doubleToLongBits(indexFooRangeKey) != Double.doubleToLongBits(other.indexFooRangeKey)) - return false; - if (Double.doubleToLongBits(indexBarRangeKey) != Double.doubleToLongBits(other.indexBarRangeKey)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - return true; - } - - @Override - public String toString() { - return "IndexRangeKeyTestClass [key=" + key + ", rangeKey=" + rangeKey + ", version=" + version - + ", indexFooRangeKey=" + indexFooRangeKey + ", indexBarRangeKey=" + indexBarRangeKey - + ", fooAttribute=" + fooAttribute + ", barAttribute=" + barAttribute + "]"; - } - + private long key; + private double rangeKey; + private Double indexFooRangeKey; + private Double indexBarRangeKey; + private Double multipleIndexRangeKey; + private Long version; + + private String fooAttribute; + private String barAttribute; + + @DynamoDBHashKey + public long getKey() { + return key; + } + + public void setKey(long key) { + this.key = key; + } + + @DynamoDBRangeKey + public double getRangeKey() { + return rangeKey; + } + + public void setRangeKey(double rangeKey) { + this.rangeKey = rangeKey; + } + + @DoNotEncrypt + @DynamoDBIndexRangeKey(localSecondaryIndexName = "index_foo", attributeName = "indexFooRangeKey") + public Double getIndexFooRangeKeyWithFakeName() { + return indexFooRangeKey; + } + + public void setIndexFooRangeKeyWithFakeName(Double indexFooRangeKey) { + this.indexFooRangeKey = indexFooRangeKey; + } + + @DoNotEncrypt + @DynamoDBIndexRangeKey(localSecondaryIndexName = "index_bar") + public Double getIndexBarRangeKey() { + return indexBarRangeKey; + } + + public void setIndexBarRangeKey(Double indexBarRangeKey) { + this.indexBarRangeKey = indexBarRangeKey; + } + + @DoNotEncrypt + @DynamoDBIndexRangeKey(localSecondaryIndexNames = {"index_foo_copy", "index_bar_copy"}) + public Double getMultipleIndexRangeKey() { + return multipleIndexRangeKey; + } + + public void setMultipleIndexRangeKey(Double multipleIndexRangeKey) { + this.multipleIndexRangeKey = multipleIndexRangeKey; + } + + @DynamoDBAttribute + public String getFooAttribute() { + return fooAttribute; + } + + public void setFooAttribute(String fooAttribute) { + this.fooAttribute = fooAttribute; + } + + @DynamoDBAttribute + public String getBarAttribute() { + return barAttribute; + } + + public void setBarAttribute(String barAttribute) { + this.barAttribute = barAttribute; + } + + @DoNotEncrypt + @DynamoDBVersionAttribute + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fooAttribute == null) ? 0 : fooAttribute.hashCode()); + result = prime * result + ((barAttribute == null) ? 0 : barAttribute.hashCode()); + result = prime * result + (int) (key ^ (key >>> 32)); + long temp; + temp = Double.doubleToLongBits(rangeKey); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(indexFooRangeKey); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(indexBarRangeKey); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + IndexRangeKeyTestClass other = (IndexRangeKeyTestClass) obj; + if (fooAttribute == null) { + if (other.fooAttribute != null) return false; + } else if (!fooAttribute.equals(other.fooAttribute)) return false; + if (barAttribute == null) { + if (other.barAttribute != null) return false; + } else if (!barAttribute.equals(other.barAttribute)) return false; + if (key != other.key) return false; + if (Double.doubleToLongBits(rangeKey) != Double.doubleToLongBits(other.rangeKey)) return false; + if (Double.doubleToLongBits(indexFooRangeKey) + != Double.doubleToLongBits(other.indexFooRangeKey)) return false; + if (Double.doubleToLongBits(indexBarRangeKey) + != Double.doubleToLongBits(other.indexBarRangeKey)) return false; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } + + @Override + public String toString() { + return "IndexRangeKeyTestClass [key=" + + key + + ", rangeKey=" + + rangeKey + + ", version=" + + version + + ", indexFooRangeKey=" + + indexFooRangeKey + + ", indexBarRangeKey=" + + indexBarRangeKey + + ", fooAttribute=" + + fooAttribute + + ", barAttribute=" + + barAttribute + + "]"; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/MapperQueryExpressionCryptoTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/MapperQueryExpressionCryptoTest.java index f6f01914..ca9142db 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/MapperQueryExpressionCryptoTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/MapperQueryExpressionCryptoTest.java @@ -12,6 +12,11 @@ */ package com.amazonaws.services.dynamodbv2.mapper.encryption; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.fail; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; @@ -27,517 +32,541 @@ import com.amazonaws.services.dynamodbv2.model.Condition; import com.amazonaws.services.dynamodbv2.model.QueryRequest; import com.amazonaws.util.ImmutableMapParameter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +/** Unit test for the private method DynamoDBMapper#createQueryRequestFromExpression */ +public class MapperQueryExpressionCryptoTest { -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; -import static org.testng.AssertJUnit.fail; + private static final String TABLE_NAME = "table_name_crypto"; + private static final Condition RANGE_KEY_CONDITION = + new Condition() + .withAttributeValueList(new AttributeValue("some value")) + .withComparisonOperator(ComparisonOperator.EQ); + + private static DynamoDBMapper mapper; + private static Method testedMethod; + + @BeforeClass + public static void setUp() throws SecurityException, NoSuchMethodException { + AmazonDynamoDB dynamo = new AmazonDynamoDBClient(); + mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + testedMethod = + DynamoDBMapper.class.getDeclaredMethod( + "createQueryRequestFromExpression", + Class.class, + DynamoDBQueryExpression.class, + DynamoDBMapperConfig.class); + testedMethod.setAccessible(true); + } + + @DynamoDBTable(tableName = TABLE_NAME) + public final class HashOnlyClass { + + @DynamoDBHashKey + @DynamoDBIndexHashKey(globalSecondaryIndexNames = "GSI-primary-hash") + private String primaryHashKey; + + @DynamoDBIndexHashKey(globalSecondaryIndexNames = {"GSI-index-hash-1", "GSI-index-hash-2"}) + private String indexHashKey; + + @DynamoDBIndexHashKey(globalSecondaryIndexNames = {"GSI-another-index-hash"}) + private String anotherIndexHashKey; + + public HashOnlyClass(String primaryHashKey, String indexHashKey, String anotherIndexHashKey) { + this.primaryHashKey = primaryHashKey; + this.indexHashKey = indexHashKey; + this.anotherIndexHashKey = anotherIndexHashKey; + } -/** - * Unit test for the private method DynamoDBMapper#createQueryRequestFromExpression - */ -public class MapperQueryExpressionCryptoTest { + public String getPrimaryHashKey() { + return primaryHashKey; + } - private static final String TABLE_NAME = "table_name_crypto"; - private static final Condition RANGE_KEY_CONDITION = new Condition() - .withAttributeValueList(new AttributeValue("some value")) - .withComparisonOperator(ComparisonOperator.EQ); + public void setPrimaryHashKey(String primaryHashKey) { + this.primaryHashKey = primaryHashKey; + } - private static DynamoDBMapper mapper; - private static Method testedMethod; + public String getIndexHashKey() { + return indexHashKey; + } - @BeforeClass - public static void setUp() throws SecurityException, NoSuchMethodException { - AmazonDynamoDB dynamo = new AmazonDynamoDBClient(); - mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - testedMethod = DynamoDBMapper.class.getDeclaredMethod("createQueryRequestFromExpression", Class.class, DynamoDBQueryExpression.class, DynamoDBMapperConfig.class); - testedMethod.setAccessible(true); + public void setIndexHashKey(String indexHashKey) { + this.indexHashKey = indexHashKey; } - @DynamoDBTable(tableName = TABLE_NAME) - public final class HashOnlyClass { - - @DynamoDBHashKey - @DynamoDBIndexHashKey( - globalSecondaryIndexNames = "GSI-primary-hash" - ) - private String primaryHashKey; - - @DynamoDBIndexHashKey( - globalSecondaryIndexNames = {"GSI-index-hash-1", "GSI-index-hash-2"} - ) - private String indexHashKey; - - @DynamoDBIndexHashKey( - globalSecondaryIndexNames = {"GSI-another-index-hash"} - ) - private String anotherIndexHashKey; - - public HashOnlyClass(String primaryHashKey, String indexHashKey, String anotherIndexHashKey) { - this.primaryHashKey = primaryHashKey; - this.indexHashKey = indexHashKey; - this.anotherIndexHashKey = anotherIndexHashKey; - } - - public String getPrimaryHashKey() { - return primaryHashKey; - } - - public void setPrimaryHashKey(String primaryHashKey) { - this.primaryHashKey = primaryHashKey; - } - - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - public String getAnotherIndexHashKey() { - return anotherIndexHashKey; - } - - public void setAnotherIndexHashKey(String anotherIndexHashKey) { - this.anotherIndexHashKey = anotherIndexHashKey; - } + public String getAnotherIndexHashKey() { + return anotherIndexHashKey; } - /** - * Tests different scenarios of hash-only query - **/ - @Test - public void testHashConditionOnly() { - // Primary hash only - QueryRequest queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", null, null))); - assertTrue(queryRequest.getKeyConditions().size() == 1); - assertEquals("primaryHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertNull(queryRequest.getIndexName()); - - // Primary hash used for a GSI - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", null, null)) - .withIndexName("GSI-primary-hash")); - assertTrue(queryRequest.getKeyConditions().size() == 1); - assertEquals("primaryHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertEquals("GSI-primary-hash", queryRequest.getIndexName()); - - // Primary hash query takes higher priority then index hash query - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", "bar", null))); - assertTrue(queryRequest.getKeyConditions().size() == 1); - assertEquals("primaryHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertNull(queryRequest.getIndexName()); - - // Ambiguous query on multiple index hash keys - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass(null, "bar", "charlie")), - "Ambiguous query expression: More than one index hash key EQ conditions"); - - // Ambiguous query when not specifying index name - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass(null, "bar", null)), - "Ambiguous query expression: More than one GSIs"); - - // Explicitly specify a GSI. - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", "bar", null)) - .withIndexName("GSI-index-hash-1")); - assertTrue(queryRequest.getKeyConditions().size() == 1); - assertEquals("indexHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("bar")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("indexHashKey")); - assertEquals("GSI-index-hash-1", queryRequest.getIndexName()); - - // Non-existent GSI - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", "bar", null)) - .withIndexName("some fake gsi"), - "No hash key condition is applicable to the specified index"); - - // No hash key condition specified - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashOnlyClass(null, null, null)), - "Illegal query expression: No hash key condition is found in the query"); + public void setAnotherIndexHashKey(String anotherIndexHashKey) { + this.anotherIndexHashKey = anotherIndexHashKey; + } + } + + /** Tests different scenarios of hash-only query */ + @Test + public void testHashConditionOnly() { + // Primary hash only + QueryRequest queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass("foo", null, null))); + assertTrue(queryRequest.getKeyConditions().size() == 1); + assertEquals("primaryHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertNull(queryRequest.getIndexName()); + + // Primary hash used for a GSI + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass("foo", null, null)) + .withIndexName("GSI-primary-hash")); + assertTrue(queryRequest.getKeyConditions().size() == 1); + assertEquals("primaryHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertEquals("GSI-primary-hash", queryRequest.getIndexName()); + + // Primary hash query takes higher priority then index hash query + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass("foo", "bar", null))); + assertTrue(queryRequest.getKeyConditions().size() == 1); + assertEquals("primaryHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertNull(queryRequest.getIndexName()); + + // Ambiguous query on multiple index hash keys + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass(null, "bar", "charlie")), + "Ambiguous query expression: More than one index hash key EQ conditions"); + + // Ambiguous query when not specifying index name + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass(null, "bar", null)), + "Ambiguous query expression: More than one GSIs"); + + // Explicitly specify a GSI. + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass("foo", "bar", null)) + .withIndexName("GSI-index-hash-1")); + assertTrue(queryRequest.getKeyConditions().size() == 1); + assertEquals("indexHashKey", queryRequest.getKeyConditions().keySet().iterator().next()); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("bar")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("indexHashKey")); + assertEquals("GSI-index-hash-1", queryRequest.getIndexName()); + + // Non-existent GSI + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass("foo", "bar", null)) + .withIndexName("some fake gsi"), + "No hash key condition is applicable to the specified index"); + + // No hash key condition specified + queryRequest = + testCreateQueryRequestFromExpression( + HashOnlyClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashOnlyClass(null, null, null)), + "Illegal query expression: No hash key condition is found in the query"); + } + + @DynamoDBTable(tableName = TABLE_NAME) + public final class HashRangeClass { + private String primaryHashKey; + private String indexHashKey; + private String primaryRangeKey; + private String indexRangeKey; + private String anotherIndexRangeKey; + + public HashRangeClass(String primaryHashKey, String indexHashKey) { + this.primaryHashKey = primaryHashKey; + this.indexHashKey = indexHashKey; } - @DynamoDBTable(tableName = TABLE_NAME) - public final class HashRangeClass { - private String primaryHashKey; - private String indexHashKey; - private String primaryRangeKey; - private String indexRangeKey; - private String anotherIndexRangeKey; - - public HashRangeClass(String primaryHashKey, String indexHashKey) { - this.primaryHashKey = primaryHashKey; - this.indexHashKey = indexHashKey; - } - - @DynamoDBHashKey - @DynamoDBIndexHashKey( - globalSecondaryIndexNames = { - "GSI-primary-hash-index-range-1", - "GSI-primary-hash-index-range-2"} - ) - public String getPrimaryHashKey() { - return primaryHashKey; - } - - public void setPrimaryHashKey(String primaryHashKey) { - this.primaryHashKey = primaryHashKey; - } - - @DynamoDBIndexHashKey( - globalSecondaryIndexNames = { - "GSI-index-hash-primary-range", - "GSI-index-hash-index-range-1", - "GSI-index-hash-index-range-2"} - ) - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - @DynamoDBRangeKey - @DynamoDBIndexRangeKey( - globalSecondaryIndexNames = {"GSI-index-hash-primary-range"}, - localSecondaryIndexName = "LSI-primary-range" - ) - public String getPrimaryRangeKey() { - return primaryRangeKey; - } - - public void setPrimaryRangeKey(String primaryRangeKey) { - this.primaryRangeKey = primaryRangeKey; - } - - @DynamoDBIndexRangeKey( - globalSecondaryIndexNames = { - "GSI-primary-hash-index-range-1", - "GSI-index-hash-index-range-1", - "GSI-index-hash-index-range-2"}, - localSecondaryIndexNames = {"LSI-index-range-1", "LSI-index-range-2"} - ) - public String getIndexRangeKey() { - return indexRangeKey; - } - - public void setIndexRangeKey(String indexRangeKey) { - this.indexRangeKey = indexRangeKey; - } - - @DynamoDBIndexRangeKey( - localSecondaryIndexName = "LSI-index-range-3", - globalSecondaryIndexName = "GSI-primary-hash-index-range-2" - ) - public String getAnotherIndexRangeKey() { - return anotherIndexRangeKey; - } - - public void setAnotherIndexRangeKey(String anotherIndexRangeKey) { - this.anotherIndexRangeKey = anotherIndexRangeKey; - } + @DynamoDBHashKey + @DynamoDBIndexHashKey( + globalSecondaryIndexNames = { + "GSI-primary-hash-index-range-1", + "GSI-primary-hash-index-range-2" + }) + public String getPrimaryHashKey() { + return primaryHashKey; } - /** - * Tests hash + range query - **/ - @Test - public void testHashAndRangeCondition() { - // Primary hash + primary range - QueryRequest queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION)); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("primaryRangeKey")); - assertNull(queryRequest.getIndexName()); - - // Primary hash + primary range on a LSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION) - .withIndexName("LSI-primary-range")); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("primaryRangeKey")); - assertEquals("LSI-primary-range", queryRequest.getIndexName()); - - // Primary hash + index range used by multiple LSI. But also a GSI hash + range - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION)); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("indexRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("indexRangeKey")); - assertEquals("GSI-primary-hash-index-range-1", queryRequest.getIndexName()); - - // Primary hash + index range on a LSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("LSI-index-range-1")); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("primaryHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("indexRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("indexRangeKey")); - assertEquals("LSI-index-range-1", queryRequest.getIndexName()); - - // Non-existent LSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("some fake lsi"), - "No range key condition is applicable to the specified index"); - - // Illegal query: Primary hash + primary range on a GSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("GSI-index-hash-index-range-1"), - "Illegal query expression: No hash key condition is applicable to the specified index"); - - // GSI hash + GSI range - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass(null, "foo")) - .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION)); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("indexHashKey")); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("indexHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("primaryRangeKey")); - assertEquals("GSI-index-hash-primary-range", queryRequest.getIndexName()); - - // Ambiguous query: GSI hash + index range used by multiple GSIs - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass(null, "foo")) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION), - "Illegal query expression: Cannot infer the index name from the query expression."); - - // Explicitly specify the GSI name - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass(null, "foo")) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("GSI-index-hash-index-range-2")); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("indexHashKey")); - assertEquals( - new Condition().withAttributeValueList(new AttributeValue("foo")) - .withComparisonOperator(ComparisonOperator.EQ), - queryRequest.getKeyConditions().get("indexHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("indexRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("indexRangeKey")); - assertEquals("GSI-index-hash-index-range-2", queryRequest.getIndexName()); - - // Ambiguous query: (1) primary hash + LSI range OR (2) GSI hash + range - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("anotherIndexRangeKey", RANGE_KEY_CONDITION), - "Ambiguous query expression: Found multiple valid queries:"); - - // Multiple range key conditions specified - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyConditions( - ImmutableMapParameter.of( - "primaryRangeKey", RANGE_KEY_CONDITION, - "indexRangeKey", RANGE_KEY_CONDITION)), - "Illegal query expression: Conditions on multiple range keys"); - - // Using an un-annotated range key - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexHashKey", RANGE_KEY_CONDITION), - "not annotated with either @DynamoDBRangeKey or @DynamoDBIndexRangeKey."); + public void setPrimaryHashKey(String primaryHashKey) { + this.primaryHashKey = primaryHashKey; } - @DynamoDBTable(tableName = TABLE_NAME) - public final class LSIRangeKeyTestClass { - private String primaryHashKey; - private String primaryRangeKey; - private String lsiRangeKey; - - public LSIRangeKeyTestClass(String primaryHashKey, String primaryRangeKey) { - this.primaryHashKey = primaryHashKey; - this.primaryRangeKey = primaryRangeKey; - } - - @DynamoDBHashKey - public String getPrimaryHashKey() { - return primaryHashKey; - } - - public void setPrimaryHashKey(String primaryHashKey) { - this.primaryHashKey = primaryHashKey; - } - - @DynamoDBRangeKey - public String getPrimaryRangeKey() { - return primaryRangeKey; - } - - public void setPrimaryRangeKey(String primaryRangeKey) { - this.primaryRangeKey = primaryRangeKey; - } - - @DynamoDBIndexRangeKey(localSecondaryIndexName = "LSI") - public String getLsiRangeKey() { - return lsiRangeKey; - } - - public void setLsiRangeKey(String lsiRangeKey) { - this.lsiRangeKey = lsiRangeKey; - } + @DynamoDBIndexHashKey( + globalSecondaryIndexNames = { + "GSI-index-hash-primary-range", + "GSI-index-hash-index-range-1", + "GSI-index-hash-index-range-2" + }) + public String getIndexHashKey() { + return indexHashKey; } - @Test - public void testHashOnlyQueryOnHashRangeTable() { - // Primary hash only query on a Hash+Range table - QueryRequest queryRequest = testCreateQueryRequestFromExpression( - LSIRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new LSIRangeKeyTestClass("foo", null))); - assertTrue(queryRequest.getKeyConditions().size() == 1); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertNull(queryRequest.getIndexName()); - - // Hash+Range query on a LSI - queryRequest = testCreateQueryRequestFromExpression( - LSIRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new LSIRangeKeyTestClass("foo", null)) - .withRangeKeyCondition("lsiRangeKey", RANGE_KEY_CONDITION) - .withIndexName("LSI")); - assertTrue(queryRequest.getKeyConditions().size() == 2); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertTrue(queryRequest.getKeyConditions().containsKey("lsiRangeKey")); - assertEquals("LSI", queryRequest.getIndexName()); - - // Hash-only query on a LSI - queryRequest = testCreateQueryRequestFromExpression( - LSIRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(new LSIRangeKeyTestClass("foo", null)) - .withIndexName("LSI")); - assertTrue(queryRequest.getKeyConditions().size() == 1); - assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); - assertEquals("LSI", queryRequest.getIndexName()); + public void setIndexHashKey(String indexHashKey) { + this.indexHashKey = indexHashKey; } - private static QueryRequest testCreateQueryRequestFromExpression( - Class clazz, DynamoDBQueryExpression queryExpression) { - return testCreateQueryRequestFromExpression(clazz, queryExpression, null); + @DynamoDBRangeKey + @DynamoDBIndexRangeKey( + globalSecondaryIndexNames = {"GSI-index-hash-primary-range"}, + localSecondaryIndexName = "LSI-primary-range") + public String getPrimaryRangeKey() { + return primaryRangeKey; } - private static QueryRequest testCreateQueryRequestFromExpression( - Class clazz, DynamoDBQueryExpression queryExpression, - String expectedErrorMessage) { - try { - QueryRequest request = (QueryRequest) testedMethod.invoke(mapper, clazz, queryExpression, DynamoDBMapperConfig.DEFAULT); - if (expectedErrorMessage != null) { - fail("Exception containing messsage (" - + expectedErrorMessage + ") is expected."); - } - return request; - } catch (InvocationTargetException ite) { - if (expectedErrorMessage != null) { - assertTrue("Exception message [" + ite.getCause().getMessage() + "] does not contain " + - "the expected message [" + expectedErrorMessage + "].", - ite.getCause().getMessage().contains(expectedErrorMessage)); - } else { - ite.getCause().printStackTrace(); - fail("Internal error when calling createQueryRequestFromExpressio method"); - } - } catch (Exception e) { - fail(e.getMessage()); - } - return null; + public void setPrimaryRangeKey(String primaryRangeKey) { + this.primaryRangeKey = primaryRangeKey; + } + + @DynamoDBIndexRangeKey( + globalSecondaryIndexNames = { + "GSI-primary-hash-index-range-1", + "GSI-index-hash-index-range-1", + "GSI-index-hash-index-range-2" + }, + localSecondaryIndexNames = {"LSI-index-range-1", "LSI-index-range-2"}) + public String getIndexRangeKey() { + return indexRangeKey; + } + + public void setIndexRangeKey(String indexRangeKey) { + this.indexRangeKey = indexRangeKey; + } + + @DynamoDBIndexRangeKey( + localSecondaryIndexName = "LSI-index-range-3", + globalSecondaryIndexName = "GSI-primary-hash-index-range-2") + public String getAnotherIndexRangeKey() { + return anotherIndexRangeKey; + } + + public void setAnotherIndexRangeKey(String anotherIndexRangeKey) { + this.anotherIndexRangeKey = anotherIndexRangeKey; + } + } + + /** Tests hash + range query */ + @Test + public void testHashAndRangeCondition() { + // Primary hash + primary range + QueryRequest queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION)); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryRangeKey")); + assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("primaryRangeKey")); + assertNull(queryRequest.getIndexName()); + + // Primary hash + primary range on a LSI + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION) + .withIndexName("LSI-primary-range")); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryRangeKey")); + assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("primaryRangeKey")); + assertEquals("LSI-primary-range", queryRequest.getIndexName()); + + // Primary hash + index range used by multiple LSI. But also a GSI hash + range + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION)); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("indexRangeKey")); + assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("indexRangeKey")); + assertEquals("GSI-primary-hash-index-range-1", queryRequest.getIndexName()); + + // Primary hash + index range on a LSI + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) + .withIndexName("LSI-index-range-1")); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("primaryHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("indexRangeKey")); + assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("indexRangeKey")); + assertEquals("LSI-index-range-1", queryRequest.getIndexName()); + + // Non-existent LSI + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) + .withIndexName("some fake lsi"), + "No range key condition is applicable to the specified index"); + + // Illegal query: Primary hash + primary range on a GSI + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) + .withIndexName("GSI-index-hash-index-range-1"), + "Illegal query expression: No hash key condition is applicable to the specified index"); + + // GSI hash + GSI range + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass(null, "foo")) + .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION)); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("indexHashKey")); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("indexHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryRangeKey")); + assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("primaryRangeKey")); + assertEquals("GSI-index-hash-primary-range", queryRequest.getIndexName()); + + // Ambiguous query: GSI hash + index range used by multiple GSIs + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass(null, "foo")) + .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION), + "Illegal query expression: Cannot infer the index name from the query expression."); + + // Explicitly specify the GSI name + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass(null, "foo")) + .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) + .withIndexName("GSI-index-hash-index-range-2")); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("indexHashKey")); + assertEquals( + new Condition() + .withAttributeValueList(new AttributeValue("foo")) + .withComparisonOperator(ComparisonOperator.EQ), + queryRequest.getKeyConditions().get("indexHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("indexRangeKey")); + assertEquals(RANGE_KEY_CONDITION, queryRequest.getKeyConditions().get("indexRangeKey")); + assertEquals("GSI-index-hash-index-range-2", queryRequest.getIndexName()); + + // Ambiguous query: (1) primary hash + LSI range OR (2) GSI hash + range + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("anotherIndexRangeKey", RANGE_KEY_CONDITION), + "Ambiguous query expression: Found multiple valid queries:"); + + // Multiple range key conditions specified + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyConditions( + ImmutableMapParameter.of( + "primaryRangeKey", RANGE_KEY_CONDITION, + "indexRangeKey", RANGE_KEY_CONDITION)), + "Illegal query expression: Conditions on multiple range keys"); + + // Using an un-annotated range key + queryRequest = + testCreateQueryRequestFromExpression( + HashRangeClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new HashRangeClass("foo", null)) + .withRangeKeyCondition("indexHashKey", RANGE_KEY_CONDITION), + "not annotated with either @DynamoDBRangeKey or @DynamoDBIndexRangeKey."); + } + + @DynamoDBTable(tableName = TABLE_NAME) + public final class LSIRangeKeyTestClass { + private String primaryHashKey; + private String primaryRangeKey; + private String lsiRangeKey; + + public LSIRangeKeyTestClass(String primaryHashKey, String primaryRangeKey) { + this.primaryHashKey = primaryHashKey; + this.primaryRangeKey = primaryRangeKey; + } + + @DynamoDBHashKey + public String getPrimaryHashKey() { + return primaryHashKey; + } + + public void setPrimaryHashKey(String primaryHashKey) { + this.primaryHashKey = primaryHashKey; + } + + @DynamoDBRangeKey + public String getPrimaryRangeKey() { + return primaryRangeKey; + } + + public void setPrimaryRangeKey(String primaryRangeKey) { + this.primaryRangeKey = primaryRangeKey; + } + + @DynamoDBIndexRangeKey(localSecondaryIndexName = "LSI") + public String getLsiRangeKey() { + return lsiRangeKey; + } + + public void setLsiRangeKey(String lsiRangeKey) { + this.lsiRangeKey = lsiRangeKey; + } + } + + @Test + public void testHashOnlyQueryOnHashRangeTable() { + // Primary hash only query on a Hash+Range table + QueryRequest queryRequest = + testCreateQueryRequestFromExpression( + LSIRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new LSIRangeKeyTestClass("foo", null))); + assertTrue(queryRequest.getKeyConditions().size() == 1); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertNull(queryRequest.getIndexName()); + + // Hash+Range query on a LSI + queryRequest = + testCreateQueryRequestFromExpression( + LSIRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new LSIRangeKeyTestClass("foo", null)) + .withRangeKeyCondition("lsiRangeKey", RANGE_KEY_CONDITION) + .withIndexName("LSI")); + assertTrue(queryRequest.getKeyConditions().size() == 2); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertTrue(queryRequest.getKeyConditions().containsKey("lsiRangeKey")); + assertEquals("LSI", queryRequest.getIndexName()); + + // Hash-only query on a LSI + queryRequest = + testCreateQueryRequestFromExpression( + LSIRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(new LSIRangeKeyTestClass("foo", null)) + .withIndexName("LSI")); + assertTrue(queryRequest.getKeyConditions().size() == 1); + assertTrue(queryRequest.getKeyConditions().containsKey("primaryHashKey")); + assertEquals("LSI", queryRequest.getIndexName()); + } + + private static QueryRequest testCreateQueryRequestFromExpression( + Class clazz, DynamoDBQueryExpression queryExpression) { + return testCreateQueryRequestFromExpression(clazz, queryExpression, null); + } + + private static QueryRequest testCreateQueryRequestFromExpression( + Class clazz, DynamoDBQueryExpression queryExpression, String expectedErrorMessage) { + try { + QueryRequest request = + (QueryRequest) + testedMethod.invoke(mapper, clazz, queryExpression, DynamoDBMapperConfig.DEFAULT); + if (expectedErrorMessage != null) { + fail("Exception containing messsage (" + expectedErrorMessage + ") is expected."); + } + return request; + } catch (InvocationTargetException ite) { + if (expectedErrorMessage != null) { + assertTrue( + "Exception message [" + + ite.getCause().getMessage() + + "] does not contain " + + "the expected message [" + + expectedErrorMessage + + "].", + ite.getCause().getMessage().contains(expectedErrorMessage)); + } else { + ite.getCause().printStackTrace(); + fail("Internal error when calling createQueryRequestFromExpressio method"); + } + } catch (Exception e) { + fail(e.getMessage()); } + return null; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NoSuchTableTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NoSuchTableTestClass.java index 9512d7eb..14cede8d 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NoSuchTableTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NoSuchTableTestClass.java @@ -18,15 +18,14 @@ @DynamoDBTable(tableName = "tableNotExist") public class NoSuchTableTestClass { - private String key; + private String key; - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberAttributeTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberAttributeTestClass.java index 4b37a2ef..ae551694 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberAttributeTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberAttributeTestClass.java @@ -17,321 +17,287 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.Calendar; import java.util.Date; -/** - * Simple domain class with numeric attributes - */ +/** Simple domain class with numeric attributes */ @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public class NumberAttributeTestClass { - private String key; - private int intAttribute; - private Integer integerAttribute; - private double doubleAttribute; - private Double doubleObjectAttribute; - private float floatAttribute; - private Float floatObjectAttribute; - private BigDecimal bigDecimalAttribute; - private BigInteger bigIntegerAttribute; - private long longAttribute; - private Long longObjectAttribute; - private short shortAttribute; - private Short shortObjectAttribute; - private byte byteAttribute; - private Byte byteObjectAttribute; - private Date dateAttribute; - private Calendar calendarAttribute; - private Boolean booleanObjectAttribute; - private boolean booleanAttribute; - private String ignored = "notSent"; - - @DynamoDBAutoGeneratedKey - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public int getIntAttribute() { - return intAttribute; - } - - public void setIntAttribute(int intAttribute) { - this.intAttribute = intAttribute; - } - - public Integer getIntegerAttribute() { - return integerAttribute; - } - - public void setIntegerAttribute(Integer integerAttribute) { - this.integerAttribute = integerAttribute; - } - - public double getDoubleAttribute() { - return doubleAttribute; - } - - public void setDoubleAttribute(double doubleAttribute) { - this.doubleAttribute = doubleAttribute; - } - - public Double getDoubleObjectAttribute() { - return doubleObjectAttribute; - } - - public void setDoubleObjectAttribute(Double doubleObjectAttribute) { - this.doubleObjectAttribute = doubleObjectAttribute; - } - - @DynamoDBAttribute - public float getFloatAttribute() { - return floatAttribute; - } - - public void setFloatAttribute(float floatAttribute) { - this.floatAttribute = floatAttribute; - } - - public Float getFloatObjectAttribute() { - return floatObjectAttribute; - } - - public void setFloatObjectAttribute(Float floatObjectAttribute) { - this.floatObjectAttribute = floatObjectAttribute; - } - - public BigDecimal getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - public BigInteger getBigIntegerAttribute() { - return bigIntegerAttribute; - } - - public void setBigIntegerAttribute(BigInteger bigIntegerAttribute) { - this.bigIntegerAttribute = bigIntegerAttribute; - } - - public long getLongAttribute() { - return longAttribute; - } - - public void setLongAttribute(long longAttribute) { - this.longAttribute = longAttribute; - } - - public Long getLongObjectAttribute() { - return longObjectAttribute; - } - - public void setLongObjectAttribute(Long longObjectAttribute) { - this.longObjectAttribute = longObjectAttribute; - } - - public byte getByteAttribute() { - return byteAttribute; - } - - public void setByteAttribute(byte byteAttribute) { - this.byteAttribute = byteAttribute; - } - - public Byte getByteObjectAttribute() { - return byteObjectAttribute; - } - - public void setByteObjectAttribute(Byte byteObjectAttribute) { - this.byteObjectAttribute = byteObjectAttribute; - } - - public Date getDateAttribute() { - return dateAttribute; - } - - public void setDateAttribute(Date dateAttribute) { - this.dateAttribute = dateAttribute; - } - - public Calendar getCalendarAttribute() { - return calendarAttribute; - } - - public void setCalendarAttribute(Calendar calendarAttribute) { - this.calendarAttribute = calendarAttribute; - } - - public Boolean getBooleanObjectAttribute() { - return booleanObjectAttribute; - } - - public void setBooleanObjectAttribute(Boolean booleanObjectAttribute) { - this.booleanObjectAttribute = booleanObjectAttribute; - } - - public boolean isBooleanAttribute() { - return booleanAttribute; - } - - public void setBooleanAttribute(boolean booleanAttribute) { - this.booleanAttribute = booleanAttribute; - } - - @DynamoDBIgnore - public String getIgnored() { - return ignored; - } - - public void setIgnored(String ignored) { - this.ignored = ignored; - } - - public short getShortAttribute() { - return shortAttribute; - } - - public void setShortAttribute(short shortAttribute) { - this.shortAttribute = shortAttribute; - } - - public Short getShortObjectAttribute() { - return shortObjectAttribute; - } - - public void setShortObjectAttribute(Short shortObjectAttribute) { - this.shortObjectAttribute = shortObjectAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); - result = prime * result + (booleanAttribute ? 1231 : 1237); - result = prime * result + ((booleanObjectAttribute == null) ? 0 : booleanObjectAttribute.hashCode()); - result = prime * result + byteAttribute; - result = prime * result + ((byteObjectAttribute == null) ? 0 : byteObjectAttribute.hashCode()); - result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); - result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); - long temp; - temp = Double.doubleToLongBits(doubleAttribute); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((doubleObjectAttribute == null) ? 0 : doubleObjectAttribute.hashCode()); - result = prime * result + Float.floatToIntBits(floatAttribute); - result = prime * result + ((floatObjectAttribute == null) ? 0 : floatObjectAttribute.hashCode()); - result = prime * result + ((ignored == null) ? 0 : ignored.hashCode()); - result = prime * result + intAttribute; - result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + (int) (longAttribute ^ (longAttribute >>> 32)); - result = prime * result + ((longObjectAttribute == null) ? 0 : longObjectAttribute.hashCode()); - result = prime * result + shortAttribute; - result = prime * result + ((shortObjectAttribute == null) ? 0 : shortObjectAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NumberAttributeTestClass other = (NumberAttributeTestClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) - return false; - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) - return false; - if (bigIntegerAttribute == null) { - if (other.bigIntegerAttribute != null) - return false; - } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) - return false; - if (booleanAttribute != other.booleanAttribute) - return false; - if (booleanObjectAttribute == null) { - if (other.booleanObjectAttribute != null) - return false; - } else if (!booleanObjectAttribute.equals(other.booleanObjectAttribute)) - return false; - if (byteAttribute != other.byteAttribute) - return false; - if (byteObjectAttribute == null) { - if (other.byteObjectAttribute != null) - return false; - } else if (!byteObjectAttribute.equals(other.byteObjectAttribute)) - return false; - if (calendarAttribute == null) { - if (other.calendarAttribute != null) - return false; - } else if (!calendarAttribute.equals(other.calendarAttribute)) - return false; - if (dateAttribute == null) { - if (other.dateAttribute != null) - return false; - } else if (!dateAttribute.equals(other.dateAttribute)) - return false; - if (Double.doubleToLongBits(doubleAttribute) != Double.doubleToLongBits(other.doubleAttribute)) - return false; - if (doubleObjectAttribute == null) { - if (other.doubleObjectAttribute != null) - return false; - } else if (!doubleObjectAttribute.equals(other.doubleObjectAttribute)) - return false; - if (Float.floatToIntBits(floatAttribute) != Float.floatToIntBits(other.floatAttribute)) - return false; - if (floatObjectAttribute == null) { - if (other.floatObjectAttribute != null) - return false; - } else if (!floatObjectAttribute.equals(other.floatObjectAttribute)) - return false; - if (ignored == null) { - if (other.ignored != null) - return false; - } else if (!ignored.equals(other.ignored)) - return false; - if (intAttribute != other.intAttribute) - return false; - if (integerAttribute == null) { - if (other.integerAttribute != null) - return false; - } else if (!integerAttribute.equals(other.integerAttribute)) - return false; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (longAttribute != other.longAttribute) - return false; - if (longObjectAttribute == null) { - if (other.longObjectAttribute != null) - return false; - } else if (!longObjectAttribute.equals(other.longObjectAttribute)) - return false; - if (shortAttribute != other.shortAttribute) - return false; - if (shortObjectAttribute == null) { - if (other.shortObjectAttribute != null) - return false; - } else if (!shortObjectAttribute.equals(other.shortObjectAttribute)) - return false; - return true; - } - + private String key; + private int intAttribute; + private Integer integerAttribute; + private double doubleAttribute; + private Double doubleObjectAttribute; + private float floatAttribute; + private Float floatObjectAttribute; + private BigDecimal bigDecimalAttribute; + private BigInteger bigIntegerAttribute; + private long longAttribute; + private Long longObjectAttribute; + private short shortAttribute; + private Short shortObjectAttribute; + private byte byteAttribute; + private Byte byteObjectAttribute; + private Date dateAttribute; + private Calendar calendarAttribute; + private Boolean booleanObjectAttribute; + private boolean booleanAttribute; + private String ignored = "notSent"; + + @DynamoDBAutoGeneratedKey + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getIntAttribute() { + return intAttribute; + } + + public void setIntAttribute(int intAttribute) { + this.intAttribute = intAttribute; + } + + public Integer getIntegerAttribute() { + return integerAttribute; + } + + public void setIntegerAttribute(Integer integerAttribute) { + this.integerAttribute = integerAttribute; + } + + public double getDoubleAttribute() { + return doubleAttribute; + } + + public void setDoubleAttribute(double doubleAttribute) { + this.doubleAttribute = doubleAttribute; + } + + public Double getDoubleObjectAttribute() { + return doubleObjectAttribute; + } + + public void setDoubleObjectAttribute(Double doubleObjectAttribute) { + this.doubleObjectAttribute = doubleObjectAttribute; + } + + @DynamoDBAttribute + public float getFloatAttribute() { + return floatAttribute; + } + + public void setFloatAttribute(float floatAttribute) { + this.floatAttribute = floatAttribute; + } + + public Float getFloatObjectAttribute() { + return floatObjectAttribute; + } + + public void setFloatObjectAttribute(Float floatObjectAttribute) { + this.floatObjectAttribute = floatObjectAttribute; + } + + public BigDecimal getBigDecimalAttribute() { + return bigDecimalAttribute; + } + + public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { + this.bigDecimalAttribute = bigDecimalAttribute; + } + + public BigInteger getBigIntegerAttribute() { + return bigIntegerAttribute; + } + + public void setBigIntegerAttribute(BigInteger bigIntegerAttribute) { + this.bigIntegerAttribute = bigIntegerAttribute; + } + + public long getLongAttribute() { + return longAttribute; + } + + public void setLongAttribute(long longAttribute) { + this.longAttribute = longAttribute; + } + + public Long getLongObjectAttribute() { + return longObjectAttribute; + } + + public void setLongObjectAttribute(Long longObjectAttribute) { + this.longObjectAttribute = longObjectAttribute; + } + + public byte getByteAttribute() { + return byteAttribute; + } + + public void setByteAttribute(byte byteAttribute) { + this.byteAttribute = byteAttribute; + } + + public Byte getByteObjectAttribute() { + return byteObjectAttribute; + } + + public void setByteObjectAttribute(Byte byteObjectAttribute) { + this.byteObjectAttribute = byteObjectAttribute; + } + + public Date getDateAttribute() { + return dateAttribute; + } + + public void setDateAttribute(Date dateAttribute) { + this.dateAttribute = dateAttribute; + } + + public Calendar getCalendarAttribute() { + return calendarAttribute; + } + + public void setCalendarAttribute(Calendar calendarAttribute) { + this.calendarAttribute = calendarAttribute; + } + + public Boolean getBooleanObjectAttribute() { + return booleanObjectAttribute; + } + + public void setBooleanObjectAttribute(Boolean booleanObjectAttribute) { + this.booleanObjectAttribute = booleanObjectAttribute; + } + + public boolean isBooleanAttribute() { + return booleanAttribute; + } + + public void setBooleanAttribute(boolean booleanAttribute) { + this.booleanAttribute = booleanAttribute; + } + + @DynamoDBIgnore + public String getIgnored() { + return ignored; + } + + public void setIgnored(String ignored) { + this.ignored = ignored; + } + + public short getShortAttribute() { + return shortAttribute; + } + + public void setShortAttribute(short shortAttribute) { + this.shortAttribute = shortAttribute; + } + + public Short getShortObjectAttribute() { + return shortObjectAttribute; + } + + public void setShortObjectAttribute(Short shortObjectAttribute) { + this.shortObjectAttribute = shortObjectAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); + result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); + result = prime * result + (booleanAttribute ? 1231 : 1237); + result = + prime * result + ((booleanObjectAttribute == null) ? 0 : booleanObjectAttribute.hashCode()); + result = prime * result + byteAttribute; + result = prime * result + ((byteObjectAttribute == null) ? 0 : byteObjectAttribute.hashCode()); + result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); + result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); + long temp; + temp = Double.doubleToLongBits(doubleAttribute); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = + prime * result + ((doubleObjectAttribute == null) ? 0 : doubleObjectAttribute.hashCode()); + result = prime * result + Float.floatToIntBits(floatAttribute); + result = + prime * result + ((floatObjectAttribute == null) ? 0 : floatObjectAttribute.hashCode()); + result = prime * result + ((ignored == null) ? 0 : ignored.hashCode()); + result = prime * result + intAttribute; + result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + (int) (longAttribute ^ (longAttribute >>> 32)); + result = prime * result + ((longObjectAttribute == null) ? 0 : longObjectAttribute.hashCode()); + result = prime * result + shortAttribute; + result = + prime * result + ((shortObjectAttribute == null) ? 0 : shortObjectAttribute.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + NumberAttributeTestClass other = (NumberAttributeTestClass) obj; + if (bigDecimalAttribute == null) { + if (other.bigDecimalAttribute != null) return false; + } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) return false; + if (bigIntegerAttribute == null) { + if (other.bigIntegerAttribute != null) return false; + } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) return false; + if (booleanAttribute != other.booleanAttribute) return false; + if (booleanObjectAttribute == null) { + if (other.booleanObjectAttribute != null) return false; + } else if (!booleanObjectAttribute.equals(other.booleanObjectAttribute)) return false; + if (byteAttribute != other.byteAttribute) return false; + if (byteObjectAttribute == null) { + if (other.byteObjectAttribute != null) return false; + } else if (!byteObjectAttribute.equals(other.byteObjectAttribute)) return false; + if (calendarAttribute == null) { + if (other.calendarAttribute != null) return false; + } else if (!calendarAttribute.equals(other.calendarAttribute)) return false; + if (dateAttribute == null) { + if (other.dateAttribute != null) return false; + } else if (!dateAttribute.equals(other.dateAttribute)) return false; + if (Double.doubleToLongBits(doubleAttribute) != Double.doubleToLongBits(other.doubleAttribute)) + return false; + if (doubleObjectAttribute == null) { + if (other.doubleObjectAttribute != null) return false; + } else if (!doubleObjectAttribute.equals(other.doubleObjectAttribute)) return false; + if (Float.floatToIntBits(floatAttribute) != Float.floatToIntBits(other.floatAttribute)) + return false; + if (floatObjectAttribute == null) { + if (other.floatObjectAttribute != null) return false; + } else if (!floatObjectAttribute.equals(other.floatObjectAttribute)) return false; + if (ignored == null) { + if (other.ignored != null) return false; + } else if (!ignored.equals(other.ignored)) return false; + if (intAttribute != other.intAttribute) return false; + if (integerAttribute == null) { + if (other.integerAttribute != null) return false; + } else if (!integerAttribute.equals(other.integerAttribute)) return false; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (longAttribute != other.longAttribute) return false; + if (longObjectAttribute == null) { + if (other.longObjectAttribute != null) return false; + } else if (!longObjectAttribute.equals(other.longObjectAttribute)) return false; + if (shortAttribute != other.shortAttribute) return false; + if (shortObjectAttribute == null) { + if (other.shortObjectAttribute != null) return false; + } else if (!shortObjectAttribute.equals(other.shortObjectAttribute)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberSetAttributeTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberSetAttributeTestClass.java index 6dcf18af..4201e5a0 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberSetAttributeTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/NumberSetAttributeTestClass.java @@ -15,226 +15,204 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.Calendar; import java.util.Date; import java.util.Set; -/** - * Simple domain class with numeric attributes - */ +/** Simple domain class with numeric attributes */ @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public class NumberSetAttributeTestClass { - private String key; - private Set integerAttribute; - private Set doubleObjectAttribute; - private Set floatObjectAttribute; - private Set bigDecimalAttribute; - private Set bigIntegerAttribute; - private Set longObjectAttribute; - private Set byteObjectAttribute; - private Set dateAttribute; - private Set calendarAttribute; - private Set booleanAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAttribute - public Set getIntegerAttribute() { - return integerAttribute; - } - - public void setIntegerAttribute(Set integerAttribute) { - this.integerAttribute = integerAttribute; - } - - @DynamoDBAttribute - public Set getDoubleObjectAttribute() { - return doubleObjectAttribute; - } - - public void setDoubleObjectAttribute(Set doubleObjectAttribute) { - this.doubleObjectAttribute = doubleObjectAttribute; - } - - @DynamoDBAttribute - public Set getFloatObjectAttribute() { - return floatObjectAttribute; - } - - public void setFloatObjectAttribute(Set floatObjectAttribute) { - this.floatObjectAttribute = floatObjectAttribute; - } - - @DynamoDBAttribute - public Set getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(Set bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - @DynamoDBAttribute - public Set getBigIntegerAttribute() { - return bigIntegerAttribute; - } - - public void setBigIntegerAttribute(Set bigIntegerAttribute) { - this.bigIntegerAttribute = bigIntegerAttribute; - } - - @DynamoDBAttribute - public Set getLongObjectAttribute() { - return longObjectAttribute; - } - - public void setLongObjectAttribute(Set longObjectAttribute) { - this.longObjectAttribute = longObjectAttribute; - } - - @DynamoDBAttribute - public Set getByteObjectAttribute() { - return byteObjectAttribute; - } - - public void setByteObjectAttribute(Set byteObjectAttribute) { - this.byteObjectAttribute = byteObjectAttribute; - } - - @DynamoDBAttribute - public Set getDateAttribute() { - return dateAttribute; - } - - public void setDateAttribute(Set dateAttribute) { - this.dateAttribute = dateAttribute; - } - - @DynamoDBAttribute - public Set getCalendarAttribute() { - return calendarAttribute; - } - - public void setCalendarAttribute(Set calendarAttribute) { - this.calendarAttribute = calendarAttribute; - } - - @DynamoDBAttribute - public Set getBooleanAttribute() { - return booleanAttribute; - } - - public void setBooleanAttribute(Set booleanAttribute) { - this.booleanAttribute = booleanAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); - result = prime * result + ((booleanAttribute == null) ? 0 : booleanAttribute.hashCode()); - result = prime * result + ((byteObjectAttribute == null) ? 0 : byteObjectAttribute.hashCode()); - result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); - result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); - result = prime * result + ((doubleObjectAttribute == null) ? 0 : doubleObjectAttribute.hashCode()); - result = prime * result + ((floatObjectAttribute == null) ? 0 : floatObjectAttribute.hashCode()); - result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((longObjectAttribute == null) ? 0 : longObjectAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NumberSetAttributeTestClass other = (NumberSetAttributeTestClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) - return false; - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) - return false; - if (bigIntegerAttribute == null) { - if (other.bigIntegerAttribute != null) - return false; - } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) - return false; - if (booleanAttribute == null) { - if (other.booleanAttribute != null) - return false; - } else if (!booleanAttribute.equals(other.booleanAttribute)) - return false; - if (byteObjectAttribute == null) { - if (other.byteObjectAttribute != null) - return false; - } else if (!byteObjectAttribute.equals(other.byteObjectAttribute)) - return false; - if (calendarAttribute == null) { - if (other.calendarAttribute != null) - return false; - } else if (!calendarAttribute.equals(other.calendarAttribute)) - return false; - if (dateAttribute == null) { - if (other.dateAttribute != null) - return false; - } else if (!dateAttribute.equals(other.dateAttribute)) - return false; - if (doubleObjectAttribute == null) { - if (other.doubleObjectAttribute != null) - return false; - } else if (!doubleObjectAttribute.equals(other.doubleObjectAttribute)) - return false; - if (floatObjectAttribute == null) { - if (other.floatObjectAttribute != null) - return false; - } else if (!floatObjectAttribute.equals(other.floatObjectAttribute)) - return false; - if (integerAttribute == null) { - if (other.integerAttribute != null) - return false; - } else if (!integerAttribute.equals(other.integerAttribute)) - return false; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (longObjectAttribute == null) { - if (other.longObjectAttribute != null) - return false; - } else if (!longObjectAttribute.equals(other.longObjectAttribute)) - return false; - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "NumberSetAttributeTestClass [key=" + key; -// + ", integerAttribute=" + integerAttribute -// + ", doubleObjectAttribute=" + doubleObjectAttribute + ", floatObjectAttribute=" + floatObjectAttribute -// + ", bigDecimalAttribute=" + bigDecimalAttribute + ", bigIntegerAttribute=" + bigIntegerAttribute -// + ", longObjectAttribute=" + longObjectAttribute + ", byteObjectAttribute=" + byteObjectAttribute -// + ", dateAttribute=" + dateAttribute + ", calendarAttribute=" + calendarAttribute -// + ", booleanAttribute=" + booleanAttribute + "]"; - } + private String key; + private Set integerAttribute; + private Set doubleObjectAttribute; + private Set floatObjectAttribute; + private Set bigDecimalAttribute; + private Set bigIntegerAttribute; + private Set longObjectAttribute; + private Set byteObjectAttribute; + private Set dateAttribute; + private Set calendarAttribute; + private Set booleanAttribute; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAttribute + public Set getIntegerAttribute() { + return integerAttribute; + } + + public void setIntegerAttribute(Set integerAttribute) { + this.integerAttribute = integerAttribute; + } + + @DynamoDBAttribute + public Set getDoubleObjectAttribute() { + return doubleObjectAttribute; + } + + public void setDoubleObjectAttribute(Set doubleObjectAttribute) { + this.doubleObjectAttribute = doubleObjectAttribute; + } + + @DynamoDBAttribute + public Set getFloatObjectAttribute() { + return floatObjectAttribute; + } + + public void setFloatObjectAttribute(Set floatObjectAttribute) { + this.floatObjectAttribute = floatObjectAttribute; + } + + @DynamoDBAttribute + public Set getBigDecimalAttribute() { + return bigDecimalAttribute; + } + + public void setBigDecimalAttribute(Set bigDecimalAttribute) { + this.bigDecimalAttribute = bigDecimalAttribute; + } + + @DynamoDBAttribute + public Set getBigIntegerAttribute() { + return bigIntegerAttribute; + } + + public void setBigIntegerAttribute(Set bigIntegerAttribute) { + this.bigIntegerAttribute = bigIntegerAttribute; + } + + @DynamoDBAttribute + public Set getLongObjectAttribute() { + return longObjectAttribute; + } + + public void setLongObjectAttribute(Set longObjectAttribute) { + this.longObjectAttribute = longObjectAttribute; + } + + @DynamoDBAttribute + public Set getByteObjectAttribute() { + return byteObjectAttribute; + } + + public void setByteObjectAttribute(Set byteObjectAttribute) { + this.byteObjectAttribute = byteObjectAttribute; + } + + @DynamoDBAttribute + public Set getDateAttribute() { + return dateAttribute; + } + + public void setDateAttribute(Set dateAttribute) { + this.dateAttribute = dateAttribute; + } + + @DynamoDBAttribute + public Set getCalendarAttribute() { + return calendarAttribute; + } + + public void setCalendarAttribute(Set calendarAttribute) { + this.calendarAttribute = calendarAttribute; + } + + @DynamoDBAttribute + public Set getBooleanAttribute() { + return booleanAttribute; + } + + public void setBooleanAttribute(Set booleanAttribute) { + this.booleanAttribute = booleanAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); + result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); + result = prime * result + ((booleanAttribute == null) ? 0 : booleanAttribute.hashCode()); + result = prime * result + ((byteObjectAttribute == null) ? 0 : byteObjectAttribute.hashCode()); + result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); + result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); + result = + prime * result + ((doubleObjectAttribute == null) ? 0 : doubleObjectAttribute.hashCode()); + result = + prime * result + ((floatObjectAttribute == null) ? 0 : floatObjectAttribute.hashCode()); + result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((longObjectAttribute == null) ? 0 : longObjectAttribute.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + NumberSetAttributeTestClass other = (NumberSetAttributeTestClass) obj; + if (bigDecimalAttribute == null) { + if (other.bigDecimalAttribute != null) return false; + } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) return false; + if (bigIntegerAttribute == null) { + if (other.bigIntegerAttribute != null) return false; + } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) return false; + if (booleanAttribute == null) { + if (other.booleanAttribute != null) return false; + } else if (!booleanAttribute.equals(other.booleanAttribute)) return false; + if (byteObjectAttribute == null) { + if (other.byteObjectAttribute != null) return false; + } else if (!byteObjectAttribute.equals(other.byteObjectAttribute)) return false; + if (calendarAttribute == null) { + if (other.calendarAttribute != null) return false; + } else if (!calendarAttribute.equals(other.calendarAttribute)) return false; + if (dateAttribute == null) { + if (other.dateAttribute != null) return false; + } else if (!dateAttribute.equals(other.dateAttribute)) return false; + if (doubleObjectAttribute == null) { + if (other.doubleObjectAttribute != null) return false; + } else if (!doubleObjectAttribute.equals(other.doubleObjectAttribute)) return false; + if (floatObjectAttribute == null) { + if (other.floatObjectAttribute != null) return false; + } else if (!floatObjectAttribute.equals(other.floatObjectAttribute)) return false; + if (integerAttribute == null) { + if (other.integerAttribute != null) return false; + } else if (!integerAttribute.equals(other.integerAttribute)) return false; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (longObjectAttribute == null) { + if (other.longObjectAttribute != null) return false; + } else if (!longObjectAttribute.equals(other.longObjectAttribute)) return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "NumberSetAttributeTestClass [key=" + key; + // + ", integerAttribute=" + integerAttribute + // + ", doubleObjectAttribute=" + doubleObjectAttribute + ", + // floatObjectAttribute=" + floatObjectAttribute + // + ", bigDecimalAttribute=" + bigDecimalAttribute + ", bigIntegerAttribute=" + + // bigIntegerAttribute + // + ", longObjectAttribute=" + longObjectAttribute + ", byteObjectAttribute=" + + // byteObjectAttribute + // + ", dateAttribute=" + dateAttribute + ", calendarAttribute=" + + // calendarAttribute + // + ", booleanAttribute=" + booleanAttribute + "]"; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/RangeKeyTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/RangeKeyTestClass.java index d24fc14f..70f606c3 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/RangeKeyTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/RangeKeyTestClass.java @@ -18,152 +18,145 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBVersionAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotEncrypt; - import java.math.BigDecimal; import java.util.Set; -/** - * Comprehensive domain class - */ +/** Comprehensive domain class */ @DynamoDBTable(tableName = "aws-java-sdk-range-test-crypto") public class RangeKeyTestClass { - private long key; - private double rangeKey; - private Long version; - - private Set integerSetAttribute; - private Set stringSetAttribute; - private BigDecimal bigDecimalAttribute; - private String stringAttribute; - - @DynamoDBHashKey - public long getKey() { - return key; - } - - public void setKey(long key) { - this.key = key; - } - - @DynamoDBRangeKey - public double getRangeKey() { - return rangeKey; - } - - public void setRangeKey(double rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDBAttribute(attributeName = "integerSetAttribute") - public Set getIntegerAttribute() { - return integerSetAttribute; - } - - public void setIntegerAttribute(Set integerAttribute) { - this.integerSetAttribute = integerAttribute; - } - - @DynamoDBAttribute - public Set getStringSetAttribute() { - return stringSetAttribute; - } - - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } - - @DoNotEncrypt - @DynamoDBAttribute - public BigDecimal getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - @DynamoDBAttribute - public String getStringAttribute() { - return stringAttribute; - } - - public void setStringAttribute(String stringAttribute) { - this.stringAttribute = stringAttribute; - } - - @DoNotEncrypt - @DynamoDBVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((integerSetAttribute == null) ? 0 : integerSetAttribute.hashCode()); - result = prime * result + (int) (key ^ (key >>> 32)); - long temp; - temp = Double.doubleToLongBits(rangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((stringAttribute == null) ? 0 : stringAttribute.hashCode()); - result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - RangeKeyTestClass other = (RangeKeyTestClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) - return false; - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) - return false; - if (integerSetAttribute == null) { - if (other.integerSetAttribute != null) - return false; - } else if (!integerSetAttribute.equals(other.integerSetAttribute)) - return false; - if (key != other.key) - return false; - if (Double.doubleToLongBits(rangeKey) != Double.doubleToLongBits(other.rangeKey)) - return false; - if (stringAttribute == null) { - if (other.stringAttribute != null) - return false; - } else if (!stringAttribute.equals(other.stringAttribute)) - return false; - if (stringSetAttribute == null) { - if (other.stringSetAttribute != null) - return false; - } else if (!stringSetAttribute.equals(other.stringSetAttribute)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - return true; - } - - @Override - public String toString() { - return "RangeKeyTestClass [key=" + key + ", rangeKey=" + rangeKey + ", version=" + version - + ", integerSetAttribute=" + integerSetAttribute + ", stringSetAttribute=" + stringSetAttribute - + ", bigDecimalAttribute=" + bigDecimalAttribute + ", stringAttribute=" + stringAttribute + "]"; - } - + private long key; + private double rangeKey; + private Long version; + + private Set integerSetAttribute; + private Set stringSetAttribute; + private BigDecimal bigDecimalAttribute; + private String stringAttribute; + + @DynamoDBHashKey + public long getKey() { + return key; + } + + public void setKey(long key) { + this.key = key; + } + + @DynamoDBRangeKey + public double getRangeKey() { + return rangeKey; + } + + public void setRangeKey(double rangeKey) { + this.rangeKey = rangeKey; + } + + @DynamoDBAttribute(attributeName = "integerSetAttribute") + public Set getIntegerAttribute() { + return integerSetAttribute; + } + + public void setIntegerAttribute(Set integerAttribute) { + this.integerSetAttribute = integerAttribute; + } + + @DynamoDBAttribute + public Set getStringSetAttribute() { + return stringSetAttribute; + } + + public void setStringSetAttribute(Set stringSetAttribute) { + this.stringSetAttribute = stringSetAttribute; + } + + @DoNotEncrypt + @DynamoDBAttribute + public BigDecimal getBigDecimalAttribute() { + return bigDecimalAttribute; + } + + public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { + this.bigDecimalAttribute = bigDecimalAttribute; + } + + @DynamoDBAttribute + public String getStringAttribute() { + return stringAttribute; + } + + public void setStringAttribute(String stringAttribute) { + this.stringAttribute = stringAttribute; + } + + @DoNotEncrypt + @DynamoDBVersionAttribute + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); + result = prime * result + ((integerSetAttribute == null) ? 0 : integerSetAttribute.hashCode()); + result = prime * result + (int) (key ^ (key >>> 32)); + long temp; + temp = Double.doubleToLongBits(rangeKey); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + ((stringAttribute == null) ? 0 : stringAttribute.hashCode()); + result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + RangeKeyTestClass other = (RangeKeyTestClass) obj; + if (bigDecimalAttribute == null) { + if (other.bigDecimalAttribute != null) return false; + } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) return false; + if (integerSetAttribute == null) { + if (other.integerSetAttribute != null) return false; + } else if (!integerSetAttribute.equals(other.integerSetAttribute)) return false; + if (key != other.key) return false; + if (Double.doubleToLongBits(rangeKey) != Double.doubleToLongBits(other.rangeKey)) return false; + if (stringAttribute == null) { + if (other.stringAttribute != null) return false; + } else if (!stringAttribute.equals(other.stringAttribute)) return false; + if (stringSetAttribute == null) { + if (other.stringSetAttribute != null) return false; + } else if (!stringSetAttribute.equals(other.stringSetAttribute)) return false; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } + + @Override + public String toString() { + return "RangeKeyTestClass [key=" + + key + + ", rangeKey=" + + rangeKey + + ", version=" + + version + + ", integerSetAttribute=" + + integerSetAttribute + + ", stringSetAttribute=" + + stringSetAttribute + + ", bigDecimalAttribute=" + + bigDecimalAttribute + + ", stringAttribute=" + + stringAttribute + + "]"; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringAttributeTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringAttributeTestClass.java index 5112ebb5..e88e474e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringAttributeTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringAttributeTestClass.java @@ -16,78 +16,66 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; -/** - * Test domain class with a single string attribute and a string key - */ +/** Test domain class with a single string attribute and a string key */ @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public class StringAttributeTestClass { - private String key; - private String stringAttribute; - private String renamedAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } + private String key; + private String stringAttribute; + private String renamedAttribute; - public void setKey(String key) { - this.key = key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } - @DynamoDBAttribute - public String getStringAttribute() { - return stringAttribute; - } + public void setKey(String key) { + this.key = key; + } - public void setStringAttribute(String stringAttribute) { - this.stringAttribute = stringAttribute; - } + @DynamoDBAttribute + public String getStringAttribute() { + return stringAttribute; + } - @DynamoDBAttribute(attributeName = "originalName") - public String getRenamedAttribute() { - return renamedAttribute; - } + public void setStringAttribute(String stringAttribute) { + this.stringAttribute = stringAttribute; + } - public void setRenamedAttribute(String renamedAttribute) { - this.renamedAttribute = renamedAttribute; - } + @DynamoDBAttribute(attributeName = "originalName") + public String getRenamedAttribute() { + return renamedAttribute; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((renamedAttribute == null) ? 0 : renamedAttribute.hashCode()); - result = prime * result + ((stringAttribute == null) ? 0 : stringAttribute.hashCode()); - return result; - } + public void setRenamedAttribute(String renamedAttribute) { + this.renamedAttribute = renamedAttribute; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - StringAttributeTestClass other = (StringAttributeTestClass) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (renamedAttribute == null) { - if (other.renamedAttribute != null) - return false; - } else if (!renamedAttribute.equals(other.renamedAttribute)) - return false; - if (stringAttribute == null) { - if (other.stringAttribute != null) - return false; - } else if (!stringAttribute.equals(other.stringAttribute)) - return false; - return true; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((renamedAttribute == null) ? 0 : renamedAttribute.hashCode()); + result = prime * result + ((stringAttribute == null) ? 0 : stringAttribute.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + StringAttributeTestClass other = (StringAttributeTestClass) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (renamedAttribute == null) { + if (other.renamedAttribute != null) return false; + } else if (!renamedAttribute.equals(other.renamedAttribute)) return false; + if (stringAttribute == null) { + if (other.stringAttribute != null) return false; + } else if (!stringAttribute.equals(other.stringAttribute)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringSetAttributeTestClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringSetAttributeTestClass.java index cb3c5769..b8c6a712 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringSetAttributeTestClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/StringSetAttributeTestClass.java @@ -15,80 +15,70 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; - import java.util.Set; -/** - * Test domain class with a string set attribute and a string key - */ +/** Test domain class with a string set attribute and a string key */ @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public class StringSetAttributeTestClass { - private String key; - private Set stringSetAttribute; - private Set StringSetAttributeRenamed; + private String key; + private Set stringSetAttribute; + private Set StringSetAttributeRenamed; - @DynamoDBHashKey - public String getKey() { - return key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } - public void setKey(String key) { - this.key = key; - } + public void setKey(String key) { + this.key = key; + } - @DynamoDBAttribute - public Set getStringSetAttribute() { - return stringSetAttribute; - } + @DynamoDBAttribute + public Set getStringSetAttribute() { + return stringSetAttribute; + } - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } + public void setStringSetAttribute(Set stringSetAttribute) { + this.stringSetAttribute = stringSetAttribute; + } - @DynamoDBAttribute(attributeName = "originalName") - public Set getStringSetAttributeRenamed() { - return StringSetAttributeRenamed; - } + @DynamoDBAttribute(attributeName = "originalName") + public Set getStringSetAttributeRenamed() { + return StringSetAttributeRenamed; + } - public void setStringSetAttributeRenamed(Set stringSetAttributeRenamed) { - StringSetAttributeRenamed = stringSetAttributeRenamed; - } + public void setStringSetAttributeRenamed(Set stringSetAttributeRenamed) { + StringSetAttributeRenamed = stringSetAttributeRenamed; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((StringSetAttributeRenamed == null) ? 0 : StringSetAttributeRenamed.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = + prime * result + + ((StringSetAttributeRenamed == null) ? 0 : StringSetAttributeRenamed.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - StringSetAttributeTestClass other = (StringSetAttributeTestClass) obj; - if (StringSetAttributeRenamed == null) { - if (other.StringSetAttributeRenamed != null) - return false; - } else if (!StringSetAttributeRenamed.equals(other.StringSetAttributeRenamed)) - return false; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (stringSetAttribute == null) { - if (other.stringSetAttribute != null) - return false; - } else if (!stringSetAttribute.equals(other.stringSetAttribute)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + StringSetAttributeTestClass other = (StringSetAttributeTestClass) obj; + if (StringSetAttributeRenamed == null) { + if (other.StringSetAttributeRenamed != null) return false; + } else if (!StringSetAttributeRenamed.equals(other.StringSetAttributeRenamed)) return false; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (stringSetAttribute == null) { + if (other.stringSetAttribute != null) return false; + } else if (!stringSetAttribute.equals(other.stringSetAttribute)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestDynamoDBMapperFactory.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestDynamoDBMapperFactory.java index d2284fb5..a30ee9fc 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestDynamoDBMapperFactory.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestDynamoDBMapperFactory.java @@ -18,15 +18,18 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; public class TestDynamoDBMapperFactory { - public static DynamoDBMapper createDynamoDBMapper(AmazonDynamoDB dynamo) { - return new DynamoDBMapper(dynamo, - DynamoDBMapperConfig.builder().withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.PUT).build(), - new AttributeEncryptor(new TestEncryptionMaterialsProvider())); - } + public static DynamoDBMapper createDynamoDBMapper(AmazonDynamoDB dynamo) { + return new DynamoDBMapper( + dynamo, + DynamoDBMapperConfig.builder() + .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.PUT) + .build(), + new AttributeEncryptor(new TestEncryptionMaterialsProvider())); + } - public static DynamoDBMapper createDynamoDBMapper(AmazonDynamoDB dynamo, DynamoDBMapperConfig config) { - return new DynamoDBMapper(dynamo, - config, - new AttributeEncryptor(new TestEncryptionMaterialsProvider())); - } + public static DynamoDBMapper createDynamoDBMapper( + AmazonDynamoDB dynamo, DynamoDBMapperConfig config) { + return new DynamoDBMapper( + dynamo, config, new AttributeEncryptor(new TestEncryptionMaterialsProvider())); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestEncryptionMaterialsProvider.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestEncryptionMaterialsProvider.java index 1e00831d..2e5009fc 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestEncryptionMaterialsProvider.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/encryption/TestEncryptionMaterialsProvider.java @@ -17,59 +17,59 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider; import com.amazonaws.util.StringMapBuilder; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Map; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; public class TestEncryptionMaterialsProvider implements EncryptionMaterialsProvider { - private final EncryptionMaterials em = new EncryptionMaterials() { + private final EncryptionMaterials em = + new EncryptionMaterials() { @Override public Map getMaterialDescription() { - return new StringMapBuilder("id", "test").build(); + return new StringMapBuilder("id", "test").build(); } @Override public SecretKey getEncryptionKey() { - return new SecretKeySpec(new byte[32], "AES"); + return new SecretKeySpec(new byte[32], "AES"); } @Override public Key getSigningKey() { - return new SecretKeySpec(new byte[32], "HmacSHA256"); + return new SecretKeySpec(new byte[32], "HmacSHA256"); } - }; + }; - private final DecryptionMaterials dm = new DecryptionMaterials() { + private final DecryptionMaterials dm = + new DecryptionMaterials() { @Override public Map getMaterialDescription() { - return new StringMapBuilder("id", "test").build(); + return new StringMapBuilder("id", "test").build(); } @Override public SecretKey getDecryptionKey() { - return new SecretKeySpec(new byte[32], "AES"); + return new SecretKeySpec(new byte[32], "AES"); } @Override public Key getVerificationKey() { - return new SecretKeySpec(new byte[32], "HmacSHA256"); + return new SecretKeySpec(new byte[32], "HmacSHA256"); } - }; + }; - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - return dm; - } + @Override + public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { + return dm; + } - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - return em; - } + @Override + public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { + return em; + } - @Override - public void refresh() { - } + @Override + public void refresh() {} } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/AutoGeneratedKeysITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/AutoGeneratedKeysITCase.java index bb21c397..1f3a56b6 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/AutoGeneratedKeysITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/AutoGeneratedKeysITCase.java @@ -12,6 +12,11 @@ */ package com.amazonaws.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; @@ -32,857 +37,796 @@ import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.util.TableUtils; import com.amazonaws.util.ImmutableMapParameter; +import java.util.Collections; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import java.util.Collections; +/** Tests using auto-generated keys for range keys, hash keys, or both. */ +public class AutoGeneratedKeysITCase extends DynamoDBMapperCryptoIntegrationTestBase { -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.fail; + private static final String TABLE_NAME = "aws-java-sdk-string-range-crypto"; + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + + // Create a table + String keyName = DynamoDBMapperCryptoIntegrationTestBase.KEY_NAME; + String rangeKeyAttributeName = "rangeKey"; + + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(TABLE_NAME) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(rangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(keyName) + .withAttributeType(ScalarAttributeType.S), + new AttributeDefinition() + .withAttributeName(rangeKeyAttributeName) + .withAttributeType(ScalarAttributeType.S)); + createTableRequest.setProvisionedThroughput( + new ProvisionedThroughput().withReadCapacityUnits(10L).withWriteCapacityUnits(5L)); + + if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { + TableUtils.waitUntilActive(dynamo, TABLE_NAME); + } + } + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class HashKeyRangeKeyBothAutoGenerated { -/** - * Tests using auto-generated keys for range keys, hash keys, or both. - */ -public class AutoGeneratedKeysITCase extends DynamoDBMapperCryptoIntegrationTestBase { + private String key; + private String rangeKey; + private String otherAttribute; + + @DynamoDBAutoGeneratedKey + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAutoGeneratedKey + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + public String getOtherAttribute() { + return otherAttribute; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } - private static final String TABLE_NAME = "aws-java-sdk-string-range-crypto"; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperCryptoIntegrationTestBase.setUp(); - - // Create a table - String keyName = DynamoDBMapperCryptoIntegrationTestBase.KEY_NAME; - String rangeKeyAttributeName = "rangeKey"; - - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(TABLE_NAME) - .withKeySchema(new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(rangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName(keyName).withAttributeType( - ScalarAttributeType.S), - new AttributeDefinition().withAttributeName(rangeKeyAttributeName).withAttributeType( - ScalarAttributeType.S)); - createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(10L) - .withWriteCapacityUnits(5L)); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - } - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class HashKeyRangeKeyBothAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDBAutoGeneratedKey - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAutoGeneratedKey - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - HashKeyRangeKeyBothAutoGenerated other = (HashKeyRangeKeyBothAutoGenerated) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( otherAttribute == null ) { - if ( other.otherAttribute != null ) - return false; - } else if ( !otherAttribute.equals(other.otherAttribute) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - } - - @Test - public void testHashKeyRangeKeyBothAutogenerated() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); - obj.setOtherAttribute("blah"); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyRangeKeyBothAutoGenerated other = mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), - obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testHashKeyRangeKeyBothAutogeneratedBatchWrite() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); - obj.setOtherAttribute("blah"); - HashKeyRangeKeyBothAutoGenerated obj2 = new HashKeyRangeKeyBothAutoGenerated(); - obj2.setOtherAttribute("blah"); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - assertNull(obj2.getKey()); - assertNull(obj2.getRangeKey()); - mapper.batchSave(obj, obj2); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - assertNotNull(obj2.getKey()); - assertNotNull(obj2.getRangeKey()); - - assertEquals(mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), - obj.getRangeKey()), obj); - assertEquals(mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj2.getKey(), - obj2.getRangeKey()), obj2); - } - - /** - * Tests providing additional expected conditions when saving item with - * auto-generated keys. - */ - @Test - public void testAutogeneratedKeyWithUserProvidedExpectedConditions() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); - obj.setOtherAttribute("blah"); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - - // Add additional expected conditions via DynamoDBSaveExpression. - // Expected conditions joined by AND are compatible with the conditions - // for auto-generated keys. - DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression(); - saveExpression - .withExpected(Collections.singletonMap( - "otherAttribute", new ExpectedAttributeValue(false))) - .withConditionalOperator(ConditionalOperator.AND); - // The save should succeed since the user provided conditions are joined by AND. - mapper.save(obj, saveExpression); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyRangeKeyBothAutoGenerated other = mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), - obj.getRangeKey()); - assertEquals(other, obj); - - // Change the conditional operator to OR. - // IllegalArgumentException is expected since the additional expected - // conditions cannot be joined with the conditions for auto-generated - // keys. - saveExpression.setConditionalOperator(ConditionalOperator.OR); - try { - mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); - } catch (IllegalArgumentException expected) {} - - // User-provided OR conditions should work if they completely override the generated conditions. - saveExpression - .withExpected(ImmutableMapParameter.of( + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + HashKeyRangeKeyBothAutoGenerated other = (HashKeyRangeKeyBothAutoGenerated) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (otherAttribute == null) { + if (other.otherAttribute != null) return false; + } else if (!otherAttribute.equals(other.otherAttribute)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testHashKeyRangeKeyBothAutogenerated() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); + obj.setOtherAttribute("blah"); + + assertNull(obj.getKey()); + assertNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + HashKeyRangeKeyBothAutoGenerated other = + mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @Test + public void testHashKeyRangeKeyBothAutogeneratedBatchWrite() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); + obj.setOtherAttribute("blah"); + HashKeyRangeKeyBothAutoGenerated obj2 = new HashKeyRangeKeyBothAutoGenerated(); + obj2.setOtherAttribute("blah"); + + assertNull(obj.getKey()); + assertNull(obj.getRangeKey()); + assertNull(obj2.getKey()); + assertNull(obj2.getRangeKey()); + mapper.batchSave(obj, obj2); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + assertNotNull(obj2.getKey()); + assertNotNull(obj2.getRangeKey()); + + assertEquals( + mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), obj.getRangeKey()), obj); + assertEquals( + mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj2.getKey(), obj2.getRangeKey()), + obj2); + } + + /** Tests providing additional expected conditions when saving item with auto-generated keys. */ + @Test + public void testAutogeneratedKeyWithUserProvidedExpectedConditions() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); + obj.setOtherAttribute("blah"); + + assertNull(obj.getKey()); + assertNull(obj.getRangeKey()); + + // Add additional expected conditions via DynamoDBSaveExpression. + // Expected conditions joined by AND are compatible with the conditions + // for auto-generated keys. + DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression(); + saveExpression + .withExpected(Collections.singletonMap("otherAttribute", new ExpectedAttributeValue(false))) + .withConditionalOperator(ConditionalOperator.AND); + // The save should succeed since the user provided conditions are joined by AND. + mapper.save(obj, saveExpression); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + HashKeyRangeKeyBothAutoGenerated other = + mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + + // Change the conditional operator to OR. + // IllegalArgumentException is expected since the additional expected + // conditions cannot be joined with the conditions for auto-generated + // keys. + saveExpression.setConditionalOperator(ConditionalOperator.OR); + try { + mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); + } catch (IllegalArgumentException expected) { + } + + // User-provided OR conditions should work if they completely override the generated conditions. + saveExpression + .withExpected( + ImmutableMapParameter.of( "otherAttribute", new ExpectedAttributeValue(false), - "key", new ExpectedAttributeValue(false), - "rangeKey", new ExpectedAttributeValue(false))) - .withConditionalOperator(ConditionalOperator.OR); - mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); - - saveExpression - .withExpected(ImmutableMapParameter.of( - "otherAttribute", new ExpectedAttributeValue(new AttributeValue("non-existent-value")), - "key", new ExpectedAttributeValue(new AttributeValue("non-existent-value")), - "rangeKey", new ExpectedAttributeValue(new AttributeValue("non-existent-value")))) - .withConditionalOperator(ConditionalOperator.OR); - try { - mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); - } catch (ConditionalCheckFailedException expected) {} - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class HashKeyAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDBAutoGeneratedKey - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - HashKeyAutoGenerated other = (HashKeyAutoGenerated) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( otherAttribute == null ) { - if ( other.otherAttribute != null ) - return false; - } else if ( !otherAttribute.equals(other.otherAttribute) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - } - - @Test - public void testHashKeyAutogenerated() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - HashKeyAutoGenerated obj = new HashKeyAutoGenerated(); - obj.setOtherAttribute("blah"); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyAutoGenerated other = mapper.load(HashKeyAutoGenerated.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class RangeKeyAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAutoGeneratedKey - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - RangeKeyAutoGenerated other = (RangeKeyAutoGenerated) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( otherAttribute == null ) { - if ( other.otherAttribute != null ) - return false; - } else if ( !otherAttribute.equals(other.otherAttribute) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - } - - @Test - public void testRangeKeyAutogenerated() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - RangeKeyAutoGenerated obj = new RangeKeyAutoGenerated(); - obj.setOtherAttribute("blah"); - obj.setKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - RangeKeyAutoGenerated other = mapper.load(RangeKeyAutoGenerated.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class NothingAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - NothingAutoGenerated other = (NothingAutoGenerated) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( otherAttribute == null ) { - if ( other.otherAttribute != null ) - return false; - } else if ( !otherAttribute.equals(other.otherAttribute) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - } - - @Test - public void testNothingAutogenerated() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NothingAutoGenerated obj = new NothingAutoGenerated(); - obj.setOtherAttribute("blah"); - obj.setKey("" + System.currentTimeMillis()); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - NothingAutoGenerated other = mapper.load(NothingAutoGenerated.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testNothingAutogeneratedErrors() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NothingAutoGenerated obj = new NothingAutoGenerated(); - - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setRangeKey("" + System.currentTimeMillis()); - obj.setKey(null); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setRangeKey(""); - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setRangeKey("" + System.currentTimeMillis()); - mapper.save(obj); - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class HashKeyRangeKeyBothAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDBAutoGeneratedKey - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAutoGeneratedKey - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - HashKeyRangeKeyBothAutoGeneratedKeyOnly other = (HashKeyRangeKeyBothAutoGeneratedKeyOnly) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - } - - @Test - public void testHashKeyRangeKeyBothAutogeneratedKeyOnly() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - HashKeyRangeKeyBothAutoGeneratedKeyOnly obj = new HashKeyRangeKeyBothAutoGeneratedKeyOnly(); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyRangeKeyBothAutoGeneratedKeyOnly other = mapper.load(HashKeyRangeKeyBothAutoGeneratedKeyOnly.class, obj.getKey(), - obj.getRangeKey()); - assertEquals(other, obj); - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class HashKeyAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDBAutoGeneratedKey - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - HashKeyAutoGeneratedKeyOnly other = (HashKeyAutoGeneratedKeyOnly) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - - } - - @Test - public void testHashKeyAutogeneratedKeyOnly() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - HashKeyAutoGeneratedKeyOnly obj = new HashKeyAutoGeneratedKeyOnly(); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyAutoGeneratedKeyOnly other = mapper.load(HashKeyAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class RangeKeyAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAutoGeneratedKey - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - RangeKeyAutoGeneratedKeyOnly other = (RangeKeyAutoGeneratedKeyOnly) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - - } - - @Test - public void testRangeKeyAutogeneratedKeyOnly() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - RangeKeyAutoGeneratedKeyOnly obj = new RangeKeyAutoGeneratedKeyOnly(); - obj.setKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - RangeKeyAutoGeneratedKeyOnly other = mapper.load(RangeKeyAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") - public static class NothingAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - NothingAutoGeneratedKeyOnly other = (NothingAutoGeneratedKeyOnly) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( rangeKey == null ) { - if ( other.rangeKey != null ) - return false; - } else if ( !rangeKey.equals(other.rangeKey) ) - return false; - return true; - } - } - - @Test - public void testNothingAutogeneratedKeyOnly() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NothingAutoGeneratedKeyOnly obj = new NothingAutoGeneratedKeyOnly(); - obj.setKey("" + System.currentTimeMillis()); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - NothingAutoGeneratedKeyOnly other = mapper.load(NothingAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testNothingAutogeneratedKeyOnlyErrors() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NothingAutoGeneratedKeyOnly obj = new NothingAutoGeneratedKeyOnly(); - - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setRangeKey("" + System.currentTimeMillis()); - obj.setKey(null); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setRangeKey(""); - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDBMappingException expected) { - } - - obj.setRangeKey("" + System.currentTimeMillis()); - mapper.save(obj); + "key", new ExpectedAttributeValue(false), + "rangeKey", new ExpectedAttributeValue(false))) + .withConditionalOperator(ConditionalOperator.OR); + mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); + + saveExpression + .withExpected( + ImmutableMapParameter.of( + "otherAttribute", + new ExpectedAttributeValue(new AttributeValue("non-existent-value")), + "key", new ExpectedAttributeValue(new AttributeValue("non-existent-value")), + "rangeKey", new ExpectedAttributeValue(new AttributeValue("non-existent-value")))) + .withConditionalOperator(ConditionalOperator.OR); + try { + mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); + } catch (ConditionalCheckFailedException expected) { + } + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class HashKeyAutoGenerated { + + private String key; + private String rangeKey; + private String otherAttribute; + + @DynamoDBAutoGeneratedKey + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + public String getOtherAttribute() { + return otherAttribute; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + HashKeyAutoGenerated other = (HashKeyAutoGenerated) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (otherAttribute == null) { + if (other.otherAttribute != null) return false; + } else if (!otherAttribute.equals(other.otherAttribute)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testHashKeyAutogenerated() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + HashKeyAutoGenerated obj = new HashKeyAutoGenerated(); + obj.setOtherAttribute("blah"); + obj.setRangeKey("" + System.currentTimeMillis()); + + assertNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + HashKeyAutoGenerated other = + mapper.load(HashKeyAutoGenerated.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class RangeKeyAutoGenerated { + + private String key; + private String rangeKey; + private String otherAttribute; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAutoGeneratedKey + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + public String getOtherAttribute() { + return otherAttribute; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + RangeKeyAutoGenerated other = (RangeKeyAutoGenerated) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (otherAttribute == null) { + if (other.otherAttribute != null) return false; + } else if (!otherAttribute.equals(other.otherAttribute)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testRangeKeyAutogenerated() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + RangeKeyAutoGenerated obj = new RangeKeyAutoGenerated(); + obj.setOtherAttribute("blah"); + obj.setKey("" + System.currentTimeMillis()); + + assertNotNull(obj.getKey()); + assertNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + RangeKeyAutoGenerated other = + mapper.load(RangeKeyAutoGenerated.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class NothingAutoGenerated { + + private String key; + private String rangeKey; + private String otherAttribute; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; } + + public String getOtherAttribute() { + return otherAttribute; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + NothingAutoGenerated other = (NothingAutoGenerated) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (otherAttribute == null) { + if (other.otherAttribute != null) return false; + } else if (!otherAttribute.equals(other.otherAttribute)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testNothingAutogenerated() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NothingAutoGenerated obj = new NothingAutoGenerated(); + obj.setOtherAttribute("blah"); + obj.setKey("" + System.currentTimeMillis()); + obj.setRangeKey("" + System.currentTimeMillis()); + + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + NothingAutoGenerated other = + mapper.load(NothingAutoGenerated.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @Test + public void testNothingAutogeneratedErrors() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NothingAutoGenerated obj = new NothingAutoGenerated(); + + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setKey("" + System.currentTimeMillis()); + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setRangeKey("" + System.currentTimeMillis()); + obj.setKey(null); + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setRangeKey(""); + obj.setKey("" + System.currentTimeMillis()); + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setRangeKey("" + System.currentTimeMillis()); + mapper.save(obj); + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class HashKeyRangeKeyBothAutoGeneratedKeyOnly { + + private String key; + private String rangeKey; + + @DynamoDBAutoGeneratedKey + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAutoGeneratedKey + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + HashKeyRangeKeyBothAutoGeneratedKeyOnly other = (HashKeyRangeKeyBothAutoGeneratedKeyOnly) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testHashKeyRangeKeyBothAutogeneratedKeyOnly() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + HashKeyRangeKeyBothAutoGeneratedKeyOnly obj = new HashKeyRangeKeyBothAutoGeneratedKeyOnly(); + + assertNull(obj.getKey()); + assertNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + HashKeyRangeKeyBothAutoGeneratedKeyOnly other = + mapper.load(HashKeyRangeKeyBothAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class HashKeyAutoGeneratedKeyOnly { + + private String key; + private String rangeKey; + + @DynamoDBAutoGeneratedKey + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + HashKeyAutoGeneratedKeyOnly other = (HashKeyAutoGeneratedKeyOnly) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testHashKeyAutogeneratedKeyOnly() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + HashKeyAutoGeneratedKeyOnly obj = new HashKeyAutoGeneratedKeyOnly(); + obj.setRangeKey("" + System.currentTimeMillis()); + + assertNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + HashKeyAutoGeneratedKeyOnly other = + mapper.load(HashKeyAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class RangeKeyAutoGeneratedKeyOnly { + + private String key; + private String rangeKey; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAutoGeneratedKey + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + RangeKeyAutoGeneratedKeyOnly other = (RangeKeyAutoGeneratedKeyOnly) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testRangeKeyAutogeneratedKeyOnly() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + RangeKeyAutoGeneratedKeyOnly obj = new RangeKeyAutoGeneratedKeyOnly(); + obj.setKey("" + System.currentTimeMillis()); + + assertNotNull(obj.getKey()); + assertNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + RangeKeyAutoGeneratedKeyOnly other = + mapper.load(RangeKeyAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @DynamoDBTable(tableName = "aws-java-sdk-string-range-crypto") + public static class NothingAutoGeneratedKeyOnly { + + private String key; + private String rangeKey; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBRangeKey + public String getRangeKey() { + return rangeKey; + } + + public void setRangeKey(String rangeKey) { + this.rangeKey = rangeKey; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + NothingAutoGeneratedKeyOnly other = (NothingAutoGeneratedKeyOnly) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (rangeKey == null) { + if (other.rangeKey != null) return false; + } else if (!rangeKey.equals(other.rangeKey)) return false; + return true; + } + } + + @Test + public void testNothingAutogeneratedKeyOnly() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NothingAutoGeneratedKeyOnly obj = new NothingAutoGeneratedKeyOnly(); + obj.setKey("" + System.currentTimeMillis()); + obj.setRangeKey("" + System.currentTimeMillis()); + + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + mapper.save(obj); + assertNotNull(obj.getKey()); + assertNotNull(obj.getRangeKey()); + + NothingAutoGeneratedKeyOnly other = + mapper.load(NothingAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); + assertEquals(other, obj); + } + + @Test + public void testNothingAutogeneratedKeyOnlyErrors() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NothingAutoGeneratedKeyOnly obj = new NothingAutoGeneratedKeyOnly(); + + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setKey("" + System.currentTimeMillis()); + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setRangeKey("" + System.currentTimeMillis()); + obj.setKey(null); + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setRangeKey(""); + obj.setKey("" + System.currentTimeMillis()); + try { + mapper.save(obj); + fail("Expected a mapping exception"); + } catch (DynamoDBMappingException expected) { + } + + obj.setRangeKey("" + System.currentTimeMillis()); + mapper.save(obj); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BatchWriteITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BatchWriteITCase.java index 0f612e77..f9f17ec4 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BatchWriteITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BatchWriteITCase.java @@ -1,17 +1,24 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.FailedBatch; import com.amazonaws.services.dynamodbv2.mapper.encryption.BinaryAttributeByteBufferTestClass; @@ -19,9 +26,6 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.NumberSetAttributeTestClass; import com.amazonaws.services.dynamodbv2.mapper.encryption.RangeKeyTestClass; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -34,400 +38,426 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -/** - * Tests batch write calls - */ +/** Tests batch write calls */ public class BatchWriteITCase extends DynamoDBMapperCryptoIntegrationTestBase { - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = 1; - private static int startKeyDebug = 1; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - } - - @Test - public void testBatchSave() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 40; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - List failedBatches = mapper.batchSave(objs); - - assertTrue(0 == failedBatches.size()); - - for (NumberSetAttributeTestClass obj : objs) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testBatchSaveAsArray() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 40; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NumberSetAttributeTestClass[] objsArray = objs.toArray(new NumberSetAttributeTestClass[objs.size()]); - mapper.batchSave((Object[])objsArray); - - for (NumberSetAttributeTestClass obj : objs) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testBatchSaveAsListFromArray() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 40; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NumberSetAttributeTestClass[] objsArray = objs.toArray(new NumberSetAttributeTestClass[objs.size()]); - mapper.batchSave(Arrays.asList(objsArray)); - - for (NumberSetAttributeTestClass obj : objs) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testBatchDelete() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 40; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.batchSave(objs); - - for ( NumberSetAttributeTestClass obj : objs ) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // Delete the odd ones - int i = 0; - List toDelete = new LinkedList(); - for ( NumberSetAttributeTestClass obj : objs ) { - if (i++ % 2 == 0) toDelete.add(obj); - } - - mapper.batchDelete(toDelete); - - i = 0; - for ( NumberSetAttributeTestClass obj : objs ) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - if (i++ % 2 == 0) { - assertNull(loaded); - } else { - assertEquals(obj, loaded); - } - } - } - - @Test - public void testBatchSaveAndDelete() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 40; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.batchSave(objs); - - for ( NumberSetAttributeTestClass obj : objs ) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // Delete the odd ones - int i = 0; - List toDelete = new LinkedList(); - for ( NumberSetAttributeTestClass obj : objs ) { - if (i++ % 2 == 0) toDelete.add(obj); - } - - // And add a bunch of new ones - List toSave = new LinkedList(); - for ( i = 0; i < 50; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - toSave.add(obj); - } - - mapper.batchWrite(toSave, toDelete); - - i = 0; - for ( NumberSetAttributeTestClass obj : objs ) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - if (i++ % 2 == 0) { - assertNull(loaded); - } else { - assertEquals(obj, loaded); - } - } - - for ( NumberSetAttributeTestClass obj : toSave ) { - NumberSetAttributeTestClass loaded = mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testMultipleTables() throws Exception { - - List objs = new ArrayList(); - int numItems = 10; - for ( int i = 0; i < numItems; i++ ) { - NumberSetAttributeTestClass obj = getUniqueNumericObject(); - objs.add(obj); - } - for ( int i = 0; i < numItems; i++ ) { - RangeKeyTestClass obj = getUniqueRangeKeyObject(); - objs.add(obj); - } - Collections.shuffle(objs); - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - List failedBatches = mapper.batchSave(objs); - assertTrue(failedBatches.size() == 0); - - for ( Object obj : objs ) { - Object loaded = null; - if ( obj instanceof NumberSetAttributeTestClass ) { - loaded = mapper.load(NumberSetAttributeTestClass.class, ((NumberSetAttributeTestClass) obj).getKey()); - } else if ( obj instanceof RangeKeyTestClass ) { - loaded = mapper.load(RangeKeyTestClass.class, ((RangeKeyTestClass) obj).getKey(), - ((RangeKeyTestClass) obj).getRangeKey()); - } else { - fail(); - } - assertEquals(obj, loaded); - } - - // Delete the odd ones - int i = 0; - List toDelete = new LinkedList(); - for ( Object obj : objs ) { - if (i++ % 2 == 0) toDelete.add(obj); - } - - // And add a bunch of new ones - List toSave = new LinkedList(); - for ( i = 0; i < numItems; i++ ) { - if ( i % 2 == 0 ) - toSave.add(getUniqueNumericObject()); - else - toSave.add(getUniqueRangeKeyObject()); - } - - failedBatches = mapper.batchWrite(toSave, toDelete); - assertTrue(0 == failedBatches.size()); - - i = 0; - for ( Object obj : objs ) { - Object loaded = null; - if ( obj instanceof NumberSetAttributeTestClass ) { - loaded = mapper.load(NumberSetAttributeTestClass.class, ((NumberSetAttributeTestClass) obj).getKey()); - } else if ( obj instanceof RangeKeyTestClass ) { - loaded = mapper.load(RangeKeyTestClass.class, ((RangeKeyTestClass) obj).getKey(), - ((RangeKeyTestClass) obj).getRangeKey()); - } else { - fail(); - } - - if (i++ % 2 == 0) { - assertNull(loaded); - } else { - assertEquals(obj, loaded); - } - } - - for ( Object obj : toSave ) { - Object loaded = null; - if ( obj instanceof NumberSetAttributeTestClass ) { - loaded = mapper.load(NumberSetAttributeTestClass.class, ((NumberSetAttributeTestClass) obj).getKey()); - } else if ( obj instanceof RangeKeyTestClass ) { - loaded = mapper.load(RangeKeyTestClass.class, ((RangeKeyTestClass) obj).getKey(), - ((RangeKeyTestClass) obj).getRangeKey()); - } else { - fail(); - } - assertEquals(obj, loaded); - } - } - - /** - * Test whether it finish processing all the items even if the first batch is failed. - */ - @Test - public void testErrorHandling() { - - List objs = new ArrayList(); - int numItems = 25; - - for (int i = 0; i < numItems; i++) { - NoSuchTableTestClass obj = getuniqueBadObject(); - objs.add(obj); - } - - for (int i = 0; i < numItems; i++) { - RangeKeyTestClass obj = getUniqueRangeKeyObject(); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - // The failed batch - List failedBatches = mapper.batchSave(objs); - assertTrue(1 == failedBatches.size()); - assertTrue(numItems == failedBatches.get(0).getUnprocessedItems().get("tableNotExist").size()); - - // The second batch succeeds, get them back - for (Object obj : objs.subList(25, 50)) { - RangeKeyTestClass loaded = mapper.load(RangeKeyTestClass.class, ((RangeKeyTestClass) obj).getKey(), ((RangeKeyTestClass) obj).getRangeKey()); - assertEquals(obj, loaded); - } - } - - /** - * Test whether we can split large batch request into small pieces. - */ - @Test - public void testLargeRequestEntity() { - - // The total batch size is beyond 1M, test whether our client can split - // the batch correctly - List objs = new ArrayList(); - - int numItems = 25; - final int CONTENT_LENGTH = 1024 * 25; - - for (int i = 0; i < numItems; i++) { - BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(CONTENT_LENGTH); - objs.add(obj); - } - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - List failedBatches = mapper.batchSave(objs); - assertEquals(failedBatches.size(), 0); - - // Get these objects back - for (BinaryAttributeByteBufferTestClass obj : objs) { - BinaryAttributeByteBufferTestClass loaded = mapper.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // There are three super large item together with some small ones, test - // whether we can successfully - // save these small items. - objs.clear(); - numItems = 10; - List largeObjs = new ArrayList(); - - // Put three super large item(beyond 64k) - largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); - largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); - largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); - for (int i = 0; i < numItems - 3; i++) { - BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(CONTENT_LENGTH / 25); - objs.add(obj); - } - - objs.addAll(largeObjs); - - failedBatches = mapper.batchSave(objs); - final int size = failedBatches.size(); - if (DEBUG) - System.err.println("failedBatches.size()=" + size); - assertThat(size, equalTo(1)); - objs.removeAll(largeObjs); - mapper.batchSave(objs); - - // Get these small objects back - for (BinaryAttributeByteBufferTestClass obj : objs) { - BinaryAttributeByteBufferTestClass loaded = mapper.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // The whole batch is super large objects, none of them will be - // processed - largeObjs.clear(); - for (int i = 0; i < 5; i++) { - BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(CONTENT_LENGTH * 30); - largeObjs.add(obj); - } - if (DEBUG) - System.err.println("failedBatches.size()=" + size); - assertThat(failedBatches.size(), equalTo(1)); - } - - - - private NoSuchTableTestClass getuniqueBadObject() { - NoSuchTableTestClass obj = new NoSuchTableTestClass(); - obj.setKey(String.valueOf(startKeyDebug++)); - return obj; - } - - private NumberSetAttributeTestClass getUniqueNumericObject() { - NumberSetAttributeTestClass obj = new NumberSetAttributeTestClass(); - obj.setKey(String.valueOf(startKeyDebug++)); - obj.setBigDecimalAttribute(toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); - obj.setBigIntegerAttribute(toSet(new BigInteger("" + startKey++), new BigInteger("" + startKey++), new BigInteger("" + startKey++))); - obj.setByteObjectAttribute(toSet(new Byte(nextByte()), new Byte(nextByte()), new Byte(nextByte()))); - obj.setDoubleObjectAttribute(toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); - obj.setFloatObjectAttribute(toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); - obj.setIntegerAttribute(toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); - obj.setLongObjectAttribute(toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); - obj.setBooleanAttribute(toSet(true, false)); - obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); - Set cals = new HashSet(); - for ( Date d : obj.getDateAttribute() ) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(d); - cals.add(cal); - } - obj.setCalendarAttribute(toSet(cals)); - return obj; - } - - private RangeKeyTestClass getUniqueRangeKeyObject() { - RangeKeyTestClass obj = new RangeKeyTestClass(); - obj.setKey(startKey++); - obj.setIntegerAttribute(toSet(start++, start++, start++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setRangeKey(start++); - obj.setStringAttribute("" + startKey++); - obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); - return obj; - } - - private String nextByte() { - return "" + byteStart++ % Byte.MAX_VALUE; + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; + private static int byteStart = 1; + private static int startKeyDebug = 1; + + @BeforeClass + public static void setUp() throws Exception { + setUpTableWithRangeAttribute(); + } + + @Test + public void testBatchSave() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 40; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + List failedBatches = mapper.batchSave(objs); + + assertTrue(0 == failedBatches.size()); + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + } + + @Test + public void testBatchSaveAsArray() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 40; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NumberSetAttributeTestClass[] objsArray = + objs.toArray(new NumberSetAttributeTestClass[objs.size()]); + mapper.batchSave((Object[]) objsArray); + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + } + + @Test + public void testBatchSaveAsListFromArray() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 40; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NumberSetAttributeTestClass[] objsArray = + objs.toArray(new NumberSetAttributeTestClass[objs.size()]); + mapper.batchSave(Arrays.asList(objsArray)); + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + } + + @Test + public void testBatchDelete() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 40; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.batchSave(objs); + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + + // Delete the odd ones + int i = 0; + List toDelete = new LinkedList(); + for (NumberSetAttributeTestClass obj : objs) { + if (i++ % 2 == 0) toDelete.add(obj); + } + + mapper.batchDelete(toDelete); + + i = 0; + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + if (i++ % 2 == 0) { + assertNull(loaded); + } else { + assertEquals(obj, loaded); + } + } + } + + @Test + public void testBatchSaveAndDelete() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 40; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.batchSave(objs); + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + + // Delete the odd ones + int i = 0; + List toDelete = new LinkedList(); + for (NumberSetAttributeTestClass obj : objs) { + if (i++ % 2 == 0) toDelete.add(obj); + } + + // And add a bunch of new ones + List toSave = new LinkedList(); + for (i = 0; i < 50; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + toSave.add(obj); + } + + mapper.batchWrite(toSave, toDelete); + + i = 0; + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + if (i++ % 2 == 0) { + assertNull(loaded); + } else { + assertEquals(obj, loaded); + } + } + + for (NumberSetAttributeTestClass obj : toSave) { + NumberSetAttributeTestClass loaded = + mapper.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + } + + @Test + public void testMultipleTables() throws Exception { + + List objs = new ArrayList(); + int numItems = 10; + for (int i = 0; i < numItems; i++) { + NumberSetAttributeTestClass obj = getUniqueNumericObject(); + objs.add(obj); + } + for (int i = 0; i < numItems; i++) { + RangeKeyTestClass obj = getUniqueRangeKeyObject(); + objs.add(obj); + } + Collections.shuffle(objs); + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + List failedBatches = mapper.batchSave(objs); + assertTrue(failedBatches.size() == 0); + + for (Object obj : objs) { + Object loaded = null; + if (obj instanceof NumberSetAttributeTestClass) { + loaded = + mapper.load( + NumberSetAttributeTestClass.class, ((NumberSetAttributeTestClass) obj).getKey()); + } else if (obj instanceof RangeKeyTestClass) { + loaded = + mapper.load( + RangeKeyTestClass.class, + ((RangeKeyTestClass) obj).getKey(), + ((RangeKeyTestClass) obj).getRangeKey()); + } else { + fail(); + } + assertEquals(obj, loaded); + } + + // Delete the odd ones + int i = 0; + List toDelete = new LinkedList(); + for (Object obj : objs) { + if (i++ % 2 == 0) toDelete.add(obj); + } + + // And add a bunch of new ones + List toSave = new LinkedList(); + for (i = 0; i < numItems; i++) { + if (i % 2 == 0) toSave.add(getUniqueNumericObject()); + else toSave.add(getUniqueRangeKeyObject()); + } + + failedBatches = mapper.batchWrite(toSave, toDelete); + assertTrue(0 == failedBatches.size()); + + i = 0; + for (Object obj : objs) { + Object loaded = null; + if (obj instanceof NumberSetAttributeTestClass) { + loaded = + mapper.load( + NumberSetAttributeTestClass.class, ((NumberSetAttributeTestClass) obj).getKey()); + } else if (obj instanceof RangeKeyTestClass) { + loaded = + mapper.load( + RangeKeyTestClass.class, + ((RangeKeyTestClass) obj).getKey(), + ((RangeKeyTestClass) obj).getRangeKey()); + } else { + fail(); + } + + if (i++ % 2 == 0) { + assertNull(loaded); + } else { + assertEquals(obj, loaded); + } + } + + for (Object obj : toSave) { + Object loaded = null; + if (obj instanceof NumberSetAttributeTestClass) { + loaded = + mapper.load( + NumberSetAttributeTestClass.class, ((NumberSetAttributeTestClass) obj).getKey()); + } else if (obj instanceof RangeKeyTestClass) { + loaded = + mapper.load( + RangeKeyTestClass.class, + ((RangeKeyTestClass) obj).getKey(), + ((RangeKeyTestClass) obj).getRangeKey()); + } else { + fail(); + } + assertEquals(obj, loaded); + } + } + + /** Test whether it finish processing all the items even if the first batch is failed. */ + @Test + public void testErrorHandling() { + + List objs = new ArrayList(); + int numItems = 25; + + for (int i = 0; i < numItems; i++) { + NoSuchTableTestClass obj = getuniqueBadObject(); + objs.add(obj); + } + + for (int i = 0; i < numItems; i++) { + RangeKeyTestClass obj = getUniqueRangeKeyObject(); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + // The failed batch + List failedBatches = mapper.batchSave(objs); + assertTrue(1 == failedBatches.size()); + assertTrue(numItems == failedBatches.get(0).getUnprocessedItems().get("tableNotExist").size()); + + // The second batch succeeds, get them back + for (Object obj : objs.subList(25, 50)) { + RangeKeyTestClass loaded = + mapper.load( + RangeKeyTestClass.class, + ((RangeKeyTestClass) obj).getKey(), + ((RangeKeyTestClass) obj).getRangeKey()); + assertEquals(obj, loaded); + } + } + + /** Test whether we can split large batch request into small pieces. */ + @Test + public void testLargeRequestEntity() { + + // The total batch size is beyond 1M, test whether our client can split + // the batch correctly + List objs = + new ArrayList(); + + int numItems = 25; + final int CONTENT_LENGTH = 1024 * 25; + + for (int i = 0; i < numItems; i++) { + BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(CONTENT_LENGTH); + objs.add(obj); + } + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + List failedBatches = mapper.batchSave(objs); + assertEquals(failedBatches.size(), 0); + + // Get these objects back + for (BinaryAttributeByteBufferTestClass obj : objs) { + BinaryAttributeByteBufferTestClass loaded = + mapper.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + + // There are three super large item together with some small ones, test + // whether we can successfully + // save these small items. + objs.clear(); + numItems = 10; + List largeObjs = + new ArrayList(); + + // Put three super large item(beyond 64k) + largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); + largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); + largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); + for (int i = 0; i < numItems - 3; i++) { + BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(CONTENT_LENGTH / 25); + objs.add(obj); + } + + objs.addAll(largeObjs); + + failedBatches = mapper.batchSave(objs); + final int size = failedBatches.size(); + if (DEBUG) System.err.println("failedBatches.size()=" + size); + assertThat(size, equalTo(1)); + objs.removeAll(largeObjs); + mapper.batchSave(objs); + + // Get these small objects back + for (BinaryAttributeByteBufferTestClass obj : objs) { + BinaryAttributeByteBufferTestClass loaded = + mapper.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + + // The whole batch is super large objects, none of them will be + // processed + largeObjs.clear(); + for (int i = 0; i < 5; i++) { + BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(CONTENT_LENGTH * 30); + largeObjs.add(obj); + } + if (DEBUG) System.err.println("failedBatches.size()=" + size); + assertThat(failedBatches.size(), equalTo(1)); + } + + private NoSuchTableTestClass getuniqueBadObject() { + NoSuchTableTestClass obj = new NoSuchTableTestClass(); + obj.setKey(String.valueOf(startKeyDebug++)); + return obj; + } + + private NumberSetAttributeTestClass getUniqueNumericObject() { + NumberSetAttributeTestClass obj = new NumberSetAttributeTestClass(); + obj.setKey(String.valueOf(startKeyDebug++)); + obj.setBigDecimalAttribute( + toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); + obj.setBigIntegerAttribute( + toSet( + new BigInteger("" + startKey++), + new BigInteger("" + startKey++), + new BigInteger("" + startKey++))); + obj.setByteObjectAttribute( + toSet(new Byte(nextByte()), new Byte(nextByte()), new Byte(nextByte()))); + obj.setDoubleObjectAttribute( + toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); + obj.setFloatObjectAttribute( + toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); + obj.setIntegerAttribute( + toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); + obj.setLongObjectAttribute( + toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); + obj.setBooleanAttribute(toSet(true, false)); + obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); + Set cals = new HashSet(); + for (Date d : obj.getDateAttribute()) { + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(d); + cals.add(cal); } + obj.setCalendarAttribute(toSet(cals)); + return obj; + } + + private RangeKeyTestClass getUniqueRangeKeyObject() { + RangeKeyTestClass obj = new RangeKeyTestClass(); + obj.setKey(startKey++); + obj.setIntegerAttribute(toSet(start++, start++, start++)); + obj.setBigDecimalAttribute(new BigDecimal(startKey++)); + obj.setRangeKey(start++); + obj.setStringAttribute("" + startKey++); + obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); + return obj; + } + + private String nextByte() { + return "" + byteStart++ % Byte.MAX_VALUE; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BinaryAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BinaryAttributesITCase.java index 24524785..c40bce9f 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BinaryAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/BinaryAttributesITCase.java @@ -1,17 +1,22 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -21,9 +26,6 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -34,249 +36,263 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - - -/** - * Tests simple string attributes - */ +/** Tests simple string attributes */ public class BinaryAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static final String BINARY_ATTRIBUTE = "binaryAttribute"; - private static final String BINARY_SET_ATTRIBUTE = "binarySetAttribute"; - private static final List> attrs = new LinkedList>(); - private static final int contentLength = 512; - // Test data - static { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - attr.put(BINARY_ATTRIBUTE, new AttributeValue().withB(ByteBuffer.wrap(generateByteArray(contentLength)))); - attr.put(BINARY_SET_ATTRIBUTE, new AttributeValue(). - withBS(ByteBuffer.wrap(generateByteArray(contentLength)), - ByteBuffer.wrap(generateByteArray(contentLength + 1)))); - attrs.add(attr); - - }; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperCryptoIntegrationTestBase.setUp(); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() - .withHashKeyName(KEY_NAME) - .withTableName(TABLE_NAME) - .build(); - // Insert the data - for ( Map attr : attrs ) { - attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); - } + private static final String BINARY_ATTRIBUTE = "binaryAttribute"; + private static final String BINARY_SET_ATTRIBUTE = "binarySetAttribute"; + private static final List> attrs = + new LinkedList>(); + private static final int contentLength = 512; + // Test data + static { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + attr.put( + BINARY_ATTRIBUTE, + new AttributeValue().withB(ByteBuffer.wrap(generateByteArray(contentLength)))); + attr.put( + BINARY_SET_ATTRIBUTE, + new AttributeValue() + .withBS( + ByteBuffer.wrap(generateByteArray(contentLength)), + ByteBuffer.wrap(generateByteArray(contentLength + 1)))); + attrs.add(attr); + } + ; + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + // Insert the data + for (Map attr : attrs) { + attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); } - - @Test - public void testLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - for ( Map attr : attrs ) { - // test BinaryAttributeClass - BinaryAttributeByteBufferTestClass x = util.load(BinaryAttributeByteBufferTestClass.class, attr.get(KEY_NAME).getS()); - assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); - assertEquals(x.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength))); - assertTrue(x.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(contentLength)))); - assertTrue(x.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(contentLength + 1)))); - - // test BinaryAttributeByteArrayTestClass - BinaryAttributeByteArrayTestClass y = util.load(BinaryAttributeByteArrayTestClass.class, attr.get(KEY_NAME).getS()); - assertEquals(y.getKey(), attr.get(KEY_NAME).getS()); - assertTrue(Arrays.equals(y.getBinaryAttribute(), (generateByteArray(contentLength)))); - assertTrue(2 == y.getBinarySetAttribute().size()); - assertTrue(setContainsBytes(y.getBinarySetAttribute(), generateByteArray(contentLength))); - assertTrue(setContainsBytes(y.getBinarySetAttribute(), generateByteArray(contentLength+1))); - } - + } + + @Test + public void testLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map attr : attrs) { + // test BinaryAttributeClass + BinaryAttributeByteBufferTestClass x = + util.load(BinaryAttributeByteBufferTestClass.class, attr.get(KEY_NAME).getS()); + assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); + assertEquals(x.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength))); + assertTrue( + x.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(contentLength)))); + assertTrue( + x.getBinarySetAttribute() + .contains(ByteBuffer.wrap(generateByteArray(contentLength + 1)))); + + // test BinaryAttributeByteArrayTestClass + BinaryAttributeByteArrayTestClass y = + util.load(BinaryAttributeByteArrayTestClass.class, attr.get(KEY_NAME).getS()); + assertEquals(y.getKey(), attr.get(KEY_NAME).getS()); + assertTrue(Arrays.equals(y.getBinaryAttribute(), (generateByteArray(contentLength)))); + assertTrue(2 == y.getBinarySetAttribute().size()); + assertTrue(setContainsBytes(y.getBinarySetAttribute(), generateByteArray(contentLength))); + assertTrue(setContainsBytes(y.getBinarySetAttribute(), generateByteArray(contentLength + 1))); } - - @Test - public void testSave() { - // test BinaryAttributeClass - List byteBufferObjs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(contentLength); - byteBufferObjs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { - util.save(obj); - } - - for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { - BinaryAttributeByteBufferTestClass loaded = util.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertEquals(loaded.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength))); - assertTrue(loaded.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(contentLength)))); - } - - // test BinaryAttributeByteArrayTestClass - List bytesObjs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - BinaryAttributeByteArrayTestClass obj = getUniqueBytesObject(contentLength); - bytesObjs.add(obj); - } - - for (BinaryAttributeByteArrayTestClass obj : bytesObjs) { - util.save(obj); - } - - for (BinaryAttributeByteArrayTestClass obj : bytesObjs) { - BinaryAttributeByteArrayTestClass loaded = util.load(BinaryAttributeByteArrayTestClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertTrue(Arrays.equals(loaded.getBinaryAttribute(), (generateByteArray(contentLength)))); - assertTrue(1 == loaded.getBinarySetAttribute().size()); - assertTrue(setContainsBytes(loaded.getBinarySetAttribute(), generateByteArray(contentLength))); - } + } + + @Test + public void testSave() { + // test BinaryAttributeClass + List byteBufferObjs = + new ArrayList(); + for (int i = 0; i < 5; i++) { + BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(contentLength); + byteBufferObjs.add(obj); } - /** - * Tests saving an incomplete object into DynamoDB - */ - @Test - public void testIncompleteObject() { - // test BinaryAttributeClass - BinaryAttributeByteBufferTestClass byteBufferObj = getUniqueByteBufferObject(contentLength); - byteBufferObj.setBinarySetAttribute(null); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(byteBufferObj); - - BinaryAttributeByteBufferTestClass loadedX = util.load(BinaryAttributeByteBufferTestClass.class, byteBufferObj.getKey()); - assertEquals(loadedX.getKey(), byteBufferObj.getKey()); - assertEquals(loadedX.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength))); - assertEquals(loadedX.getBinarySetAttribute(), null); - - - // test removing an attribute - assertNotNull(byteBufferObj.getBinaryAttribute()); - byteBufferObj.setBinaryAttribute(null); - util.save(byteBufferObj); - - loadedX = util.load(BinaryAttributeByteBufferTestClass.class, byteBufferObj.getKey()); - assertEquals(loadedX.getKey(), byteBufferObj.getKey()); - assertEquals(loadedX.getBinaryAttribute(), null); - assertEquals(loadedX.getBinarySetAttribute(), null); - - // test BinaryAttributeByteArrayTestClass - BinaryAttributeByteArrayTestClass bytesObj = getUniqueBytesObject(contentLength); - bytesObj.setBinarySetAttribute(null); - util.save(bytesObj); - - BinaryAttributeByteArrayTestClass loadedY = util.load(BinaryAttributeByteArrayTestClass.class, bytesObj.getKey()); - assertEquals(loadedY.getKey(), bytesObj.getKey()); - assertTrue(Arrays.equals(loadedY.getBinaryAttribute(), generateByteArray(contentLength))); - assertEquals(loadedY.getBinarySetAttribute(), null); - - - // test removing an attribute - assertNotNull(bytesObj.getBinaryAttribute()); - bytesObj.setBinaryAttribute(null); - util.save(bytesObj); - - loadedY = util.load(BinaryAttributeByteArrayTestClass.class, bytesObj.getKey()); - assertEquals(loadedY.getKey(), bytesObj.getKey()); - assertEquals(loadedY.getBinaryAttribute(), null); - assertEquals(loadedY.getBinarySetAttribute(), null); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { + util.save(obj); } - @Test - public void testUpdate() { - // test BinaryAttributeClass - List byteBufferObjs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(contentLength); - byteBufferObjs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { - util.save(obj); - } - - for ( BinaryAttributeByteBufferTestClass obj : byteBufferObjs ) { - BinaryAttributeByteBufferTestClass replacement = getUniqueByteBufferObject(contentLength - 1); - replacement.setKey(obj.getKey()); - util.save(replacement); - - BinaryAttributeByteBufferTestClass loaded = util.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertEquals(loaded.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength - 1))); - assertTrue(loaded.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(contentLength - 1)))); - - } - - // test BinaryAttributeByteArrayTestClass - List bytesObj = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - BinaryAttributeByteArrayTestClass obj = getUniqueBytesObject(contentLength); - bytesObj.add(obj); - } - - for (BinaryAttributeByteArrayTestClass obj : bytesObj) { - util.save(obj); - } - - for ( BinaryAttributeByteArrayTestClass obj : bytesObj ) { - BinaryAttributeByteArrayTestClass replacement = getUniqueBytesObject(contentLength - 1); - replacement.setKey(obj.getKey()); - util.save(replacement); - - BinaryAttributeByteArrayTestClass loaded = util.load(BinaryAttributeByteArrayTestClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertTrue(Arrays.equals(loaded.getBinaryAttribute(), (generateByteArray(contentLength - 1)))); - assertTrue(1 == loaded.getBinarySetAttribute().size()); - assertTrue(setContainsBytes(loaded.getBinarySetAttribute(), generateByteArray(contentLength - 1))); - - } + for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { + BinaryAttributeByteBufferTestClass loaded = + util.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); + assertEquals(loaded.getKey(), obj.getKey()); + assertEquals(loaded.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength))); + assertTrue( + loaded + .getBinarySetAttribute() + .contains(ByteBuffer.wrap(generateByteArray(contentLength)))); } - @Test - public void testDelete() throws Exception { - // test BinaryAttributeClass - BinaryAttributeByteBufferTestClass byteBufferObj = getUniqueByteBufferObject(contentLength); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(byteBufferObj); + // test BinaryAttributeByteArrayTestClass + List bytesObjs = + new ArrayList(); + for (int i = 0; i < 5; i++) { + BinaryAttributeByteArrayTestClass obj = getUniqueBytesObject(contentLength); + bytesObjs.add(obj); + } - util.delete(byteBufferObj); - assertNull(util.load(BinaryAttributeByteBufferTestClass.class, byteBufferObj.getKey())); + for (BinaryAttributeByteArrayTestClass obj : bytesObjs) { + util.save(obj); + } - // test BinaryAttributeByteArrayTestClass - BinaryAttributeByteArrayTestClass bytesObj = getUniqueBytesObject(contentLength); - util.save(bytesObj); + for (BinaryAttributeByteArrayTestClass obj : bytesObjs) { + BinaryAttributeByteArrayTestClass loaded = + util.load(BinaryAttributeByteArrayTestClass.class, obj.getKey()); + assertEquals(loaded.getKey(), obj.getKey()); + assertTrue(Arrays.equals(loaded.getBinaryAttribute(), (generateByteArray(contentLength)))); + assertTrue(1 == loaded.getBinarySetAttribute().size()); + assertTrue( + setContainsBytes(loaded.getBinarySetAttribute(), generateByteArray(contentLength))); + } + } + + /** Tests saving an incomplete object into DynamoDB */ + @Test + public void testIncompleteObject() { + // test BinaryAttributeClass + BinaryAttributeByteBufferTestClass byteBufferObj = getUniqueByteBufferObject(contentLength); + byteBufferObj.setBinarySetAttribute(null); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(byteBufferObj); + + BinaryAttributeByteBufferTestClass loadedX = + util.load(BinaryAttributeByteBufferTestClass.class, byteBufferObj.getKey()); + assertEquals(loadedX.getKey(), byteBufferObj.getKey()); + assertEquals(loadedX.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength))); + assertEquals(loadedX.getBinarySetAttribute(), null); + + // test removing an attribute + assertNotNull(byteBufferObj.getBinaryAttribute()); + byteBufferObj.setBinaryAttribute(null); + util.save(byteBufferObj); + + loadedX = util.load(BinaryAttributeByteBufferTestClass.class, byteBufferObj.getKey()); + assertEquals(loadedX.getKey(), byteBufferObj.getKey()); + assertEquals(loadedX.getBinaryAttribute(), null); + assertEquals(loadedX.getBinarySetAttribute(), null); + + // test BinaryAttributeByteArrayTestClass + BinaryAttributeByteArrayTestClass bytesObj = getUniqueBytesObject(contentLength); + bytesObj.setBinarySetAttribute(null); + util.save(bytesObj); + + BinaryAttributeByteArrayTestClass loadedY = + util.load(BinaryAttributeByteArrayTestClass.class, bytesObj.getKey()); + assertEquals(loadedY.getKey(), bytesObj.getKey()); + assertTrue(Arrays.equals(loadedY.getBinaryAttribute(), generateByteArray(contentLength))); + assertEquals(loadedY.getBinarySetAttribute(), null); + + // test removing an attribute + assertNotNull(bytesObj.getBinaryAttribute()); + bytesObj.setBinaryAttribute(null); + util.save(bytesObj); + + loadedY = util.load(BinaryAttributeByteArrayTestClass.class, bytesObj.getKey()); + assertEquals(loadedY.getKey(), bytesObj.getKey()); + assertEquals(loadedY.getBinaryAttribute(), null); + assertEquals(loadedY.getBinarySetAttribute(), null); + } + + @Test + public void testUpdate() { + // test BinaryAttributeClass + List byteBufferObjs = + new ArrayList(); + for (int i = 0; i < 5; i++) { + BinaryAttributeByteBufferTestClass obj = getUniqueByteBufferObject(contentLength); + byteBufferObjs.add(obj); + } - util.delete(bytesObj); - assertNull(util.load(BinaryAttributeByteArrayTestClass.class, bytesObj.getKey())); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { + util.save(obj); + } + for (BinaryAttributeByteBufferTestClass obj : byteBufferObjs) { + BinaryAttributeByteBufferTestClass replacement = getUniqueByteBufferObject(contentLength - 1); + replacement.setKey(obj.getKey()); + util.save(replacement); + + BinaryAttributeByteBufferTestClass loaded = + util.load(BinaryAttributeByteBufferTestClass.class, obj.getKey()); + assertEquals(loaded.getKey(), obj.getKey()); + assertEquals( + loaded.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(contentLength - 1))); + assertTrue( + loaded + .getBinarySetAttribute() + .contains(ByteBuffer.wrap(generateByteArray(contentLength - 1)))); } - private BinaryAttributeByteArrayTestClass getUniqueBytesObject(int contentLength) { - BinaryAttributeByteArrayTestClass obj = new BinaryAttributeByteArrayTestClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBinaryAttribute(generateByteArray(contentLength)); - Set byteArray = new HashSet(); - byteArray.add(generateByteArray(contentLength)); - obj.setBinarySetAttribute(byteArray); - return obj; + // test BinaryAttributeByteArrayTestClass + List bytesObj = + new ArrayList(); + for (int i = 0; i < 5; i++) { + BinaryAttributeByteArrayTestClass obj = getUniqueBytesObject(contentLength); + bytesObj.add(obj); } - private boolean setContainsBytes(Set set, byte[] bytes) { - Iterator iter = set.iterator(); - while (iter.hasNext()) { - if (Arrays.equals(iter.next(), bytes)) - return true; - } - return false; + for (BinaryAttributeByteArrayTestClass obj : bytesObj) { + util.save(obj); } + for (BinaryAttributeByteArrayTestClass obj : bytesObj) { + BinaryAttributeByteArrayTestClass replacement = getUniqueBytesObject(contentLength - 1); + replacement.setKey(obj.getKey()); + util.save(replacement); + + BinaryAttributeByteArrayTestClass loaded = + util.load(BinaryAttributeByteArrayTestClass.class, obj.getKey()); + assertEquals(loaded.getKey(), obj.getKey()); + assertTrue( + Arrays.equals(loaded.getBinaryAttribute(), (generateByteArray(contentLength - 1)))); + assertTrue(1 == loaded.getBinarySetAttribute().size()); + assertTrue( + setContainsBytes(loaded.getBinarySetAttribute(), generateByteArray(contentLength - 1))); + } + } + + @Test + public void testDelete() throws Exception { + // test BinaryAttributeClass + BinaryAttributeByteBufferTestClass byteBufferObj = getUniqueByteBufferObject(contentLength); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(byteBufferObj); + + util.delete(byteBufferObj); + assertNull(util.load(BinaryAttributeByteBufferTestClass.class, byteBufferObj.getKey())); + + // test BinaryAttributeByteArrayTestClass + BinaryAttributeByteArrayTestClass bytesObj = getUniqueBytesObject(contentLength); + util.save(bytesObj); + + util.delete(bytesObj); + assertNull(util.load(BinaryAttributeByteArrayTestClass.class, bytesObj.getKey())); + } + + private BinaryAttributeByteArrayTestClass getUniqueBytesObject(int contentLength) { + BinaryAttributeByteArrayTestClass obj = new BinaryAttributeByteArrayTestClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setBinaryAttribute(generateByteArray(contentLength)); + Set byteArray = new HashSet(); + byteArray.add(generateByteArray(contentLength)); + obj.setBinarySetAttribute(byteArray); + return obj; + } + + private boolean setContainsBytes(Set set, byte[] bytes) { + Iterator iter = set.iterator(); + while (iter.hasNext()) { + if (Arrays.equals(iter.next(), bytes)) return true; + } + return false; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ComplexTypeITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ComplexTypeITCase.java index 33de2c6e..8c06c2fe 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ComplexTypeITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ComplexTypeITCase.java @@ -1,17 +1,19 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMarshalling; @@ -23,8 +25,6 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.MappingJsonFactory; -import org.testng.annotations.Test; - import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; @@ -33,323 +33,293 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.List; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +/** Tests of the configuration object */ +public class ComplexTypeITCase extends DynamoDBMapperCryptoIntegrationTestBase { + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; + private static int byteStart = -127; + + @Test + public void testComplexTypes() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + ComplexClass obj = getUniqueObject(); + util.save(obj); + ComplexClass loaded = util.load(ComplexClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + + private ComplexClass getUniqueObject() { + ComplexClass obj = new ComplexClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setBigDecimalAttribute(new BigDecimal(startKey++)); + obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); + obj.setByteAttribute((byte) byteStart++); + obj.setByteObjectAttribute(new Byte("" + byteStart++)); + obj.setDoubleAttribute(new Double("" + start++)); + obj.setDoubleObjectAttribute(new Double("" + start++)); + obj.setFloatAttribute(new Float("" + start++)); + obj.setFloatObjectAttribute(new Float("" + start++)); + obj.setIntAttribute(new Integer("" + start++)); + obj.setIntegerAttribute(new Integer("" + start++)); + obj.setLongAttribute(new Long("" + start++)); + obj.setLongObjectAttribute(new Long("" + start++)); + obj.setDateAttribute(new Date(startKey++)); + obj.setBooleanAttribute(start++ % 2 == 0); + obj.setBooleanObjectAttribute(start++ % 2 == 0); + obj.setExtraField("" + startKey++); + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(new Date(startKey++)); + obj.setCalendarAttribute(cal); + obj.setComplexNestedType( + new ComplexNestedType( + "" + start++, start++, new ComplexNestedType("" + start++, start++, null))); + List complexTypes = new ArrayList(); + complexTypes.add( + new ComplexNestedType( + "" + start++, start++, new ComplexNestedType("" + start++, start++, null))); + complexTypes.add( + new ComplexNestedType( + "" + start++, start++, new ComplexNestedType("" + start++, start++, null))); + complexTypes.add( + new ComplexNestedType( + "" + start++, start++, new ComplexNestedType("" + start++, start++, null))); + obj.setComplexNestedTypeList(complexTypes); + return obj; + } + + /* + * Type with a complex nested field for working with marshallers + */ + + public static final class ComplexNestedTypeMarshaller extends JsonMarshaller {} + + public static final class ComplexNestedListTypeMarshaller + extends JsonMarshaller> { + + /* (non-Javadoc) + * @see com.amazonaws.services.dynamodbv2.datamodeling.JsonMarshaller#unmarshall(java.lang.Class, java.lang.String) + */ + @Override + public List unmarshall(Class> clazz, String obj) { + try { + JsonFactory jsonFactory = new MappingJsonFactory(); + JsonParser jsonParser = jsonFactory.createJsonParser(new StringReader(obj)); + return jsonParser.readValueAs(new TypeReference>() {}); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } -/** - * Tests of the configuration object - */ -public class ComplexTypeITCase extends DynamoDBMapperCryptoIntegrationTestBase { + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static final class ComplexClass extends NumberAttributeTestClass { - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = -127; + private String extraField; + private ComplexNestedType complexNestedType; + private List complexNestedTypeList; - @Test - public void testComplexTypes() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + @DynamoDBMarshalling(marshallerClass = ComplexNestedTypeMarshaller.class) + public ComplexNestedType getComplexNestedType() { + return complexNestedType; + } - ComplexClass obj = getUniqueObject(); - util.save(obj); - ComplexClass loaded = util.load(ComplexClass.class, obj.getKey()); - assertEquals(obj, loaded); + public void setComplexNestedType(ComplexNestedType complexNestedType) { + this.complexNestedType = complexNestedType; } - private ComplexClass getUniqueObject() { - ComplexClass obj = new ComplexClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); - obj.setByteAttribute((byte) byteStart++); - obj.setByteObjectAttribute(new Byte("" + byteStart++)); - obj.setDoubleAttribute(new Double("" + start++)); - obj.setDoubleObjectAttribute(new Double("" + start++)); - obj.setFloatAttribute(new Float("" + start++)); - obj.setFloatObjectAttribute(new Float("" + start++)); - obj.setIntAttribute(new Integer("" + start++)); - obj.setIntegerAttribute(new Integer("" + start++)); - obj.setLongAttribute(new Long("" + start++)); - obj.setLongObjectAttribute(new Long("" + start++)); - obj.setDateAttribute(new Date(startKey++)); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanObjectAttribute(start++ % 2 == 0); - obj.setExtraField("" + startKey++); - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(new Date(startKey++)); - obj.setCalendarAttribute(cal); - obj.setComplexNestedType(new ComplexNestedType("" + start++, start++, new ComplexNestedType("" + start++, - start++, null))); - List complexTypes = new ArrayList(); - complexTypes.add(new ComplexNestedType("" + start++, start++, - new ComplexNestedType("" + start++, start++, null))); - complexTypes.add(new ComplexNestedType("" + start++, start++, new ComplexNestedType("" + start++, start++, null))); - complexTypes.add(new ComplexNestedType("" + start++, start++, new ComplexNestedType("" + start++, start++, null))); - obj.setComplexNestedTypeList(complexTypes); - return obj; + @DynamoDBMarshalling(marshallerClass = ComplexNestedListTypeMarshaller.class) + public List getComplexNestedTypeList() { + return complexNestedTypeList; + } + + public void setComplexNestedTypeList(List complexNestedTypeList) { + this.complexNestedTypeList = complexNestedTypeList; + } + + public String getExtraField() { + return extraField; + } + + public void setExtraField(String extraField) { + this.extraField = extraField; } /* - * Type with a complex nested field for working with marshallers + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((complexNestedType == null) ? 0 : complexNestedType.hashCode()); + result = + prime * result + ((complexNestedTypeList == null) ? 0 : complexNestedTypeList.hashCode()); + result = prime * result + ((extraField == null) ? 0 : extraField.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + ComplexClass other = (ComplexClass) obj; + if (complexNestedType == null) { + if (other.complexNestedType != null) return false; + } else if (!complexNestedType.equals(other.complexNestedType)) return false; + if (complexNestedTypeList == null) { + if (other.complexNestedTypeList != null) return false; + } else if (!complexNestedTypeList.equals(other.complexNestedTypeList)) return false; + if (extraField == null) { + if (other.extraField != null) return false; + } else if (!extraField.equals(other.extraField)) return false; + return true; + } + } - public static final class ComplexNestedTypeMarshaller extends JsonMarshaller { + public static final class ComplexNestedType { + private String stringValue; + private Integer intValue; + private ComplexNestedType nestedType; + + public ComplexNestedType() {} + + public ComplexNestedType(String stringValue, Integer intValue, ComplexNestedType nestedType) { + super(); + this.stringValue = stringValue; + this.intValue = intValue; + this.nestedType = nestedType; } - public static final class ComplexNestedListTypeMarshaller extends JsonMarshaller> { - - /* (non-Javadoc) - * @see com.amazonaws.services.dynamodbv2.datamodeling.JsonMarshaller#unmarshall(java.lang.Class, java.lang.String) - */ - @Override - public List unmarshall(Class> clazz, String obj) { - try { - JsonFactory jsonFactory = new MappingJsonFactory(); - JsonParser jsonParser = jsonFactory.createJsonParser(new StringReader(obj)); - return jsonParser.readValueAs(new TypeReference>() { - }); - } catch ( Exception e ) { - throw new RuntimeException(e); - } - } + public String getStringValue() { + return stringValue; } - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static final class ComplexClass extends NumberAttributeTestClass { - - private String extraField; - private ComplexNestedType complexNestedType; - private List complexNestedTypeList; - - @DynamoDBMarshalling(marshallerClass = ComplexNestedTypeMarshaller.class) - public ComplexNestedType getComplexNestedType() { - return complexNestedType; - } - - public void setComplexNestedType(ComplexNestedType complexNestedType) { - this.complexNestedType = complexNestedType; - } - - @DynamoDBMarshalling(marshallerClass = ComplexNestedListTypeMarshaller.class) - public List getComplexNestedTypeList() { - return complexNestedTypeList; - } - - public void setComplexNestedTypeList(List complexNestedTypeList) { - this.complexNestedTypeList = complexNestedTypeList; - } - - public String getExtraField() { - return extraField; - } - - public void setExtraField(String extraField) { - this.extraField = extraField; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((complexNestedType == null) ? 0 : complexNestedType.hashCode()); - result = prime * result + ((complexNestedTypeList == null) ? 0 : complexNestedTypeList.hashCode()); - result = prime * result + ((extraField == null) ? 0 : extraField.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - ComplexClass other = (ComplexClass) obj; - if ( complexNestedType == null ) { - if ( other.complexNestedType != null ) - return false; - } else if ( !complexNestedType.equals(other.complexNestedType) ) - return false; - if ( complexNestedTypeList == null ) { - if ( other.complexNestedTypeList != null ) - return false; - } else if ( !complexNestedTypeList.equals(other.complexNestedTypeList) ) - return false; - if ( extraField == null ) { - if ( other.extraField != null ) - return false; - } else if ( !extraField.equals(other.extraField) ) - return false; - return true; - } + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + public Integer getIntValue() { + return intValue; } - public static final class ComplexNestedType { - - private String stringValue; - private Integer intValue; - private ComplexNestedType nestedType; - - public ComplexNestedType() { - } - - public ComplexNestedType(String stringValue, Integer intValue, ComplexNestedType nestedType) { - super(); - this.stringValue = stringValue; - this.intValue = intValue; - this.nestedType = nestedType; - } - - public String getStringValue() { - return stringValue; - } - - public void setStringValue(String stringValue) { - this.stringValue = stringValue; - } - - public Integer getIntValue() { - return intValue; - } - - public void setIntValue(Integer intValue) { - this.intValue = intValue; - } - - public ComplexNestedType getNestedType() { - return nestedType; - } - - public void setNestedType(ComplexNestedType nestedType) { - this.nestedType = nestedType; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((intValue == null) ? 0 : intValue.hashCode()); - result = prime * result + ((nestedType == null) ? 0 : nestedType.hashCode()); - result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - ComplexNestedType other = (ComplexNestedType) obj; - if ( intValue == null ) { - if ( other.intValue != null ) - return false; - } else if ( !intValue.equals(other.intValue) ) - return false; - if ( nestedType == null ) { - if ( other.nestedType != null ) - return false; - } else if ( !nestedType.equals(other.nestedType) ) - return false; - if ( stringValue == null ) { - if ( other.stringValue != null ) - return false; - } else if ( !stringValue.equals(other.stringValue) ) - return false; - return true; - } + public void setIntValue(Integer intValue) { + this.intValue = intValue; + } + public ComplexNestedType getNestedType() { + return nestedType; } - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static final class ComplexKey { - - private ComplexNestedType key; - private String otherAttribute; - - @DynamoDBHashKey - @DynamoDBMarshalling(marshallerClass = ComplexNestedTypeMarshaller.class) - public ComplexNestedType getKey() { - return key; - } - - public void setKey(ComplexNestedType key) { - this.key = key; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - ComplexKey other = (ComplexKey) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( otherAttribute == null ) { - if ( other.otherAttribute != null ) - return false; - } else if ( !otherAttribute.equals(other.otherAttribute) ) - return false; - return true; - } + public void setNestedType(ComplexNestedType nestedType) { + this.nestedType = nestedType; } - - /** - * Tests using a complex type for a (string) key - */ - @Test - public void testComplexKey() throws Exception { - ComplexKey obj = new ComplexKey(); - ComplexNestedType key = new ComplexNestedType(); - key.setIntValue(start++); - key.setStringValue("" + start++); - obj.setKey(key); - obj.setOtherAttribute("" + start++); - - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - mapper.save(obj); - ComplexKey loaded = mapper.load(ComplexKey.class, obj.getKey()); - assertEquals(obj, loaded); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((intValue == null) ? 0 : intValue.hashCode()); + result = prime * result + ((nestedType == null) ? 0 : nestedType.hashCode()); + result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ComplexNestedType other = (ComplexNestedType) obj; + if (intValue == null) { + if (other.intValue != null) return false; + } else if (!intValue.equals(other.intValue)) return false; + if (nestedType == null) { + if (other.nestedType != null) return false; + } else if (!nestedType.equals(other.nestedType)) return false; + if (stringValue == null) { + if (other.stringValue != null) return false; + } else if (!stringValue.equals(other.stringValue)) return false; + return true; + } + } + + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static final class ComplexKey { + + private ComplexNestedType key; + private String otherAttribute; + + @DynamoDBHashKey + @DynamoDBMarshalling(marshallerClass = ComplexNestedTypeMarshaller.class) + public ComplexNestedType getKey() { + return key; + } + + public void setKey(ComplexNestedType key) { + this.key = key; + } + + public String getOtherAttribute() { + return otherAttribute; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ComplexKey other = (ComplexKey) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (otherAttribute == null) { + if (other.otherAttribute != null) return false; + } else if (!otherAttribute.equals(other.otherAttribute)) return false; + return true; } - + } + + /** Tests using a complex type for a (string) key */ + @Test + public void testComplexKey() throws Exception { + ComplexKey obj = new ComplexKey(); + ComplexNestedType key = new ComplexNestedType(); + key.setIntValue(start++); + key.setStringValue("" + start++); + obj.setKey(key); + obj.setOtherAttribute("" + start++); + + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + mapper.save(obj); + ComplexKey loaded = mapper.load(ComplexKey.class, obj.getKey()); + assertEquals(obj, loaded); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ConfigurationITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ConfigurationITCase.java index 622b2626..377888ed 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ConfigurationITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ConfigurationITCase.java @@ -1,17 +1,22 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; @@ -21,232 +26,214 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.mapper.encryption.NumberAttributeTestClass; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.UUID; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.fail; +/** Tests of the configuration object */ +public class ConfigurationITCase extends DynamoDBMapperCryptoIntegrationTestBase { + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; + private static int byteStart = -127; -/** - * Tests of the configuration object - */ -public class ConfigurationITCase extends DynamoDBMapperCryptoIntegrationTestBase { + @Test + public void testClobber() throws Exception { + DynamoDBMapper util = + new DynamoDBMapper(dynamo, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); + + NumberAttributeTestClassExtended obj = getUniqueObject(); + util.save(obj); + assertEquals(obj, util.load(obj.getClass(), obj.getKey())); - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = -127; + NumberAttributeTestClass copy = copy(obj); + util.save(copy); + assertEquals(copy, util.load(copy.getClass(), obj.getKey())); - @Test - public void testClobber() throws Exception { - DynamoDBMapper util = new DynamoDBMapper(dynamo, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); + // We should have lost the extra field because of the clobber behavior + assertNull(util.load(NumberAttributeTestClassExtended.class, obj.getKey()).getExtraField()); - NumberAttributeTestClassExtended obj = getUniqueObject(); - util.save(obj); - assertEquals(obj, util.load(obj.getClass(), obj.getKey())); + // Now test overriding the clobber behavior on a per-save basis + obj = getUniqueObject(); + util.save(obj); + assertEquals(obj, util.load(obj.getClass(), obj.getKey())); - NumberAttributeTestClass copy = copy(obj); - util.save(copy); - assertEquals(copy, util.load(copy.getClass(), obj.getKey())); + copy = copy(obj); + util.save(copy, new DynamoDBMapperConfig(SaveBehavior.UPDATE)); + assertEquals(copy, util.load(copy.getClass(), obj.getKey())); - // We should have lost the extra field because of the clobber behavior - assertNull(util.load(NumberAttributeTestClassExtended.class, obj.getKey()).getExtraField()); + // We shouldn't have lost any extra info + assertNotNull(util.load(NumberAttributeTestClassExtended.class, obj.getKey()).getExtraField()); + } - // Now test overriding the clobber behavior on a per-save basis - obj = getUniqueObject(); - util.save(obj); - assertEquals(obj, util.load(obj.getClass(), obj.getKey())); + @Test + public void testTableOverride() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - copy = copy(obj); - util.save(copy, new DynamoDBMapperConfig(SaveBehavior.UPDATE)); - assertEquals(copy, util.load(copy.getClass(), obj.getKey())); + TableOverrideTestClass obj = new TableOverrideTestClass(); + obj.setOtherField(UUID.randomUUID().toString()); - // We shouldn't have lost any extra info - assertNotNull(util.load(NumberAttributeTestClassExtended.class, obj.getKey()).getExtraField()); + try { + util.save(obj); + fail("Expected an exception"); + } catch (Exception e) { } - @Test - public void testTableOverride() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - TableOverrideTestClass obj = new TableOverrideTestClass(); - obj.setOtherField(UUID.randomUUID().toString()); - - try { - util.save(obj); - fail("Expected an exception"); - } catch ( Exception e ) { - } - - util.save(obj, new DynamoDBMapperConfig(new TableNameOverride("aws-java-sdk-util-crypto"))); - - try { - util.load(TableOverrideTestClass.class, obj.getKey()); - fail("Expected an exception"); - } catch ( Exception e ) { - } - - Object loaded = util.load(TableOverrideTestClass.class, obj.getKey(), new DynamoDBMapperConfig(TableNameOverride.withTableNamePrefix("aws-"))); - assertEquals(loaded, obj); - - try { - util.delete(obj); - fail("Expected an exception"); - } catch ( Exception e ) { - } - - util.delete(obj, new DynamoDBMapperConfig(TableNameOverride.withTableNamePrefix("aws-"))); + util.save(obj, new DynamoDBMapperConfig(new TableNameOverride("aws-java-sdk-util-crypto"))); + + try { + util.load(TableOverrideTestClass.class, obj.getKey()); + fail("Expected an exception"); + } catch (Exception e) { + } + + Object loaded = + util.load( + TableOverrideTestClass.class, + obj.getKey(), + new DynamoDBMapperConfig(TableNameOverride.withTableNamePrefix("aws-"))); + assertEquals(loaded, obj); + + try { + util.delete(obj); + fail("Expected an exception"); + } catch (Exception e) { + } + + util.delete(obj, new DynamoDBMapperConfig(TableNameOverride.withTableNamePrefix("aws-"))); + } + + private NumberAttributeTestClassExtended getUniqueObject() { + NumberAttributeTestClassExtended obj = new NumberAttributeTestClassExtended(); + obj.setKey(String.valueOf(startKey++)); + obj.setBigDecimalAttribute(new BigDecimal(startKey++)); + obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); + obj.setByteAttribute((byte) byteStart++); + obj.setByteObjectAttribute(new Byte("" + byteStart++)); + obj.setDoubleAttribute(new Double("" + start++)); + obj.setDoubleObjectAttribute(new Double("" + start++)); + obj.setFloatAttribute(new Float("" + start++)); + obj.setFloatObjectAttribute(new Float("" + start++)); + obj.setIntAttribute(new Integer("" + start++)); + obj.setIntegerAttribute(new Integer("" + start++)); + obj.setLongAttribute(new Long("" + start++)); + obj.setLongObjectAttribute(new Long("" + start++)); + obj.setDateAttribute(new Date(startKey++)); + obj.setBooleanAttribute(start++ % 2 == 0); + obj.setBooleanObjectAttribute(start++ % 2 == 0); + obj.setExtraField("" + startKey++); + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(new Date(startKey++)); + obj.setCalendarAttribute(cal); + return obj; + } + + private NumberAttributeTestClass copy(NumberAttributeTestClassExtended obj) { + NumberAttributeTestClass copy = new NumberAttributeTestClass(); + copy.setKey(obj.getKey()); + copy.setBigDecimalAttribute(obj.getBigDecimalAttribute()); + copy.setBigIntegerAttribute(obj.getBigIntegerAttribute()); + copy.setByteAttribute(obj.getByteAttribute()); + copy.setByteObjectAttribute(obj.getByteObjectAttribute()); + copy.setDoubleAttribute(obj.getDoubleAttribute()); + copy.setDoubleObjectAttribute(obj.getDoubleObjectAttribute()); + copy.setFloatAttribute(obj.getFloatAttribute()); + copy.setFloatObjectAttribute(obj.getFloatObjectAttribute()); + copy.setIntAttribute(obj.getIntAttribute()); + copy.setIntegerAttribute(obj.getIntegerAttribute()); + copy.setLongAttribute(obj.getLongAttribute()); + copy.setLongObjectAttribute(obj.getLongObjectAttribute()); + copy.setDateAttribute(obj.getDateAttribute()); + copy.setBooleanAttribute(obj.isBooleanAttribute()); + copy.setBooleanObjectAttribute(obj.getBooleanObjectAttribute()); + return copy; + } + + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static final class NumberAttributeTestClassExtended extends NumberAttributeTestClass { + + private String extraField; + + public String getExtraField() { + return extraField; } - private NumberAttributeTestClassExtended getUniqueObject() { - NumberAttributeTestClassExtended obj = new NumberAttributeTestClassExtended(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); - obj.setByteAttribute((byte) byteStart++); - obj.setByteObjectAttribute(new Byte("" + byteStart++)); - obj.setDoubleAttribute(new Double("" + start++)); - obj.setDoubleObjectAttribute(new Double("" + start++)); - obj.setFloatAttribute(new Float("" + start++)); - obj.setFloatObjectAttribute(new Float("" + start++)); - obj.setIntAttribute(new Integer("" + start++)); - obj.setIntegerAttribute(new Integer("" + start++)); - obj.setLongAttribute(new Long("" + start++)); - obj.setLongObjectAttribute(new Long("" + start++)); - obj.setDateAttribute(new Date(startKey++)); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanObjectAttribute(start++ % 2 == 0); - obj.setExtraField("" + startKey++); - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(new Date(startKey++)); - obj.setCalendarAttribute(cal); - return obj; + public void setExtraField(String extraField) { + this.extraField = extraField; } - private NumberAttributeTestClass copy(NumberAttributeTestClassExtended obj) { - NumberAttributeTestClass copy = new NumberAttributeTestClass(); - copy.setKey(obj.getKey()); - copy.setBigDecimalAttribute(obj.getBigDecimalAttribute()); - copy.setBigIntegerAttribute(obj.getBigIntegerAttribute()); - copy.setByteAttribute(obj.getByteAttribute()); - copy.setByteObjectAttribute(obj.getByteObjectAttribute()); - copy.setDoubleAttribute(obj.getDoubleAttribute()); - copy.setDoubleObjectAttribute(obj.getDoubleObjectAttribute()); - copy.setFloatAttribute(obj.getFloatAttribute()); - copy.setFloatObjectAttribute(obj.getFloatObjectAttribute()); - copy.setIntAttribute(obj.getIntAttribute()); - copy.setIntegerAttribute(obj.getIntegerAttribute()); - copy.setLongAttribute(obj.getLongAttribute()); - copy.setLongObjectAttribute(obj.getLongObjectAttribute()); - copy.setDateAttribute(obj.getDateAttribute()); - copy.setBooleanAttribute(obj.isBooleanAttribute()); - copy.setBooleanObjectAttribute(obj.getBooleanObjectAttribute()); - return copy; + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((extraField == null) ? 0 : extraField.hashCode()); + return result; } - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static final class NumberAttributeTestClassExtended extends NumberAttributeTestClass { - - private String extraField; - - public String getExtraField() { - return extraField; - } - - public void setExtraField(String extraField) { - this.extraField = extraField; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((extraField == null) ? 0 : extraField.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - NumberAttributeTestClassExtended other = (NumberAttributeTestClassExtended) obj; - if ( extraField == null ) { - if ( other.extraField != null ) - return false; - } else if ( !extraField.equals(other.extraField) ) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + NumberAttributeTestClassExtended other = (NumberAttributeTestClassExtended) obj; + if (extraField == null) { + if (other.extraField != null) return false; + } else if (!extraField.equals(other.extraField)) return false; + return true; } + } + + @DynamoDBTable(tableName = "java-sdk-util-crypto") // doesn't exist + public static final class TableOverrideTestClass { - @DynamoDBTable(tableName = "java-sdk-util-crypto") // doesn't exist - public static final class TableOverrideTestClass { - - private String key; - private String otherField; - - @DynamoDBAutoGeneratedKey - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getOtherField() { - return otherField; - } - - public void setOtherField(String otherField) { - this.otherField = otherField; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherField == null) ? 0 : otherField.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - TableOverrideTestClass other = (TableOverrideTestClass) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( otherField == null ) { - if ( other.otherField != null ) - return false; - } else if ( !otherField.equals(other.otherField) ) - return false; - return true; - } + private String key; + private String otherField; + + @DynamoDBAutoGeneratedKey + @DynamoDBHashKey + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; } + public String getOtherField() { + return otherField; + } + + public void setOtherField(String otherField) { + this.otherField = otherField; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((otherField == null) ? 0 : otherField.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TableOverrideTestClass other = (TableOverrideTestClass) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (otherField == null) { + if (other.otherField != null) return false; + } else if (!otherField.equals(other.otherField)) return false; + return true; + } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBCryptoIntegrationTestBase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBCryptoIntegrationTestBase.java index 8a212cf9..e83a77df 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBCryptoIntegrationTestBase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBCryptoIntegrationTestBase.java @@ -27,178 +27,203 @@ import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.model.TableDescription; import com.amazonaws.services.dynamodbv2.util.TableUtils; -import org.testng.annotations.BeforeClass; - import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; +import org.testng.annotations.BeforeClass; public class DynamoDBCryptoIntegrationTestBase extends DynamoDBTestBase { - protected static final boolean DEBUG = false; - protected static final String KEY_NAME = "key"; - protected static final String TABLE_NAME = "aws-java-sdk-util-crypto"; - - protected static long startKey = System.currentTimeMillis(); - - protected static final String TABLE_WITH_RANGE_ATTRIBUTE = "aws-java-sdk-range-test-crypto"; - protected static final String TABLE_WITH_INDEX_RANGE_ATTRIBUTE = "aws-java-sdk-index-range-test-crypto"; - - protected static Logger log = Logger.getLogger("DynamoDBCryptoITCaseBase"); - - @BeforeClass - public static void setUp() throws Exception { - // Create a table - DynamoDBTestBase.setUpTestBase(); - String keyName = KEY_NAME; - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(TABLE_NAME) - .withKeySchema(new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH)) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName(keyName).withAttributeType( - ScalarAttributeType.S)); - createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(10L) - .withWriteCapacityUnits(5L)); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - } + protected static final boolean DEBUG = false; + protected static final String KEY_NAME = "key"; + protected static final String TABLE_NAME = "aws-java-sdk-util-crypto"; + + protected static long startKey = System.currentTimeMillis(); + + protected static final String TABLE_WITH_RANGE_ATTRIBUTE = "aws-java-sdk-range-test-crypto"; + protected static final String TABLE_WITH_INDEX_RANGE_ATTRIBUTE = + "aws-java-sdk-index-range-test-crypto"; + + protected static Logger log = Logger.getLogger("DynamoDBCryptoITCaseBase"); + + @BeforeClass + public static void setUp() throws Exception { + // Create a table + DynamoDBTestBase.setUpTestBase(); + String keyName = KEY_NAME; + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(TABLE_NAME) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(keyName) + .withAttributeType(ScalarAttributeType.S)); + createTableRequest.setProvisionedThroughput( + new ProvisionedThroughput().withReadCapacityUnits(10L).withWriteCapacityUnits(5L)); + + if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { + TableUtils.waitUntilActive(dynamo, TABLE_NAME); } - - /** - * Utility method to delete tables used in the integration test - */ - public static void deleteCryptoIntegrationTestTables() { - List integrationTestTables = new ArrayList<>(); - integrationTestTables.add(TABLE_NAME); - integrationTestTables.add(TABLE_WITH_INDEX_RANGE_ATTRIBUTE); - integrationTestTables.add(TABLE_WITH_RANGE_ATTRIBUTE); - for (String name : integrationTestTables) { - dynamo.deleteTable(new DeleteTableRequest().withTableName(name)); - } + } + + /** Utility method to delete tables used in the integration test */ + public static void deleteCryptoIntegrationTestTables() { + List integrationTestTables = new ArrayList<>(); + integrationTestTables.add(TABLE_NAME); + integrationTestTables.add(TABLE_WITH_INDEX_RANGE_ATTRIBUTE); + integrationTestTables.add(TABLE_WITH_RANGE_ATTRIBUTE); + for (String name : integrationTestTables) { + dynamo.deleteTable(new DeleteTableRequest().withTableName(name)); } - - protected static void setUpTableWithRangeAttribute() throws Exception { - setUp(); - - String keyName = DynamoDBCryptoIntegrationTestBase.KEY_NAME; - String rangeKeyAttributeName = "rangeKey"; - - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(TABLE_WITH_RANGE_ATTRIBUTE) - .withKeySchema(new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(rangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName(keyName).withAttributeType( - ScalarAttributeType.N), - new AttributeDefinition().withAttributeName(rangeKeyAttributeName).withAttributeType( - ScalarAttributeType.N)); - createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(10L) - .withWriteCapacityUnits(5L)); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_WITH_RANGE_ATTRIBUTE); - } + } + + protected static void setUpTableWithRangeAttribute() throws Exception { + setUp(); + + String keyName = DynamoDBCryptoIntegrationTestBase.KEY_NAME; + String rangeKeyAttributeName = "rangeKey"; + + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(TABLE_WITH_RANGE_ATTRIBUTE) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(rangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(keyName) + .withAttributeType(ScalarAttributeType.N), + new AttributeDefinition() + .withAttributeName(rangeKeyAttributeName) + .withAttributeType(ScalarAttributeType.N)); + createTableRequest.setProvisionedThroughput( + new ProvisionedThroughput().withReadCapacityUnits(10L).withWriteCapacityUnits(5L)); + + if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { + TableUtils.waitUntilActive(dynamo, TABLE_WITH_RANGE_ATTRIBUTE); } + } - protected static void setUpTableWithIndexRangeAttribute(boolean recreateTable) throws Exception { - setUp(); - if (recreateTable) { - dynamo.deleteTable(new DeleteTableRequest().withTableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE)); - waitForTableToBecomeDeleted(TABLE_WITH_INDEX_RANGE_ATTRIBUTE); - } - - String keyName = DynamoDBCryptoIntegrationTestBase.KEY_NAME; - String rangeKeyAttributeName = "rangeKey"; - String indexFooRangeKeyAttributeName = "indexFooRangeKey"; - String indexBarRangeKeyAttributeName = "indexBarRangeKey"; - String multipleIndexRangeKeyAttributeName = "multipleIndexRangeKey"; - String indexFooName = "index_foo"; - String indexBarName = "index_bar"; - String indexFooCopyName = "index_foo_copy"; - String indexBarCopyName = "index_bar_copy"; - - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE) - .withKeySchema( - new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(rangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withLocalSecondaryIndexes( - new LocalSecondaryIndex() - .withIndexName(indexFooName) - .withKeySchema( - new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(indexFooRangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withProjection(new Projection().withProjectionType(ProjectionType.ALL)), - new LocalSecondaryIndex() - .withIndexName(indexBarName) - .withKeySchema( - new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(indexBarRangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withProjection(new Projection() - .withProjectionType(ProjectionType.ALL)), - new LocalSecondaryIndex() - .withIndexName(indexFooCopyName) - .withKeySchema( - new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(multipleIndexRangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withProjection(new Projection() - .withProjectionType(ProjectionType.ALL)), - new LocalSecondaryIndex() - .withIndexName(indexBarCopyName) - .withKeySchema( - new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), - new KeySchemaElement().withAttributeName(multipleIndexRangeKeyAttributeName).withKeyType(KeyType.RANGE)) - .withProjection(new Projection() - .withProjectionType(ProjectionType.ALL))) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName(keyName).withAttributeType(ScalarAttributeType.N), - new AttributeDefinition().withAttributeName(rangeKeyAttributeName).withAttributeType(ScalarAttributeType.N), - new AttributeDefinition().withAttributeName(indexFooRangeKeyAttributeName).withAttributeType(ScalarAttributeType.N), - new AttributeDefinition().withAttributeName(indexBarRangeKeyAttributeName).withAttributeType(ScalarAttributeType.N), - new AttributeDefinition().withAttributeName(multipleIndexRangeKeyAttributeName).withAttributeType(ScalarAttributeType.N)); - createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(10L) - .withWriteCapacityUnits(5L)); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_WITH_INDEX_RANGE_ATTRIBUTE); - } + protected static void setUpTableWithIndexRangeAttribute(boolean recreateTable) throws Exception { + setUp(); + if (recreateTable) { + dynamo.deleteTable(new DeleteTableRequest().withTableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE)); + waitForTableToBecomeDeleted(TABLE_WITH_INDEX_RANGE_ATTRIBUTE); } - protected static void waitForTableToBecomeDeleted(String tableName) { - waitForTableToBecomeDeleted(dynamo, tableName); + String keyName = DynamoDBCryptoIntegrationTestBase.KEY_NAME; + String rangeKeyAttributeName = "rangeKey"; + String indexFooRangeKeyAttributeName = "indexFooRangeKey"; + String indexBarRangeKeyAttributeName = "indexBarRangeKey"; + String multipleIndexRangeKeyAttributeName = "multipleIndexRangeKey"; + String indexFooName = "index_foo"; + String indexBarName = "index_bar"; + String indexFooCopyName = "index_foo_copy"; + String indexBarCopyName = "index_bar_copy"; + + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(rangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withLocalSecondaryIndexes( + new LocalSecondaryIndex() + .withIndexName(indexFooName) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(indexFooRangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withProjection(new Projection().withProjectionType(ProjectionType.ALL)), + new LocalSecondaryIndex() + .withIndexName(indexBarName) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(indexBarRangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withProjection(new Projection().withProjectionType(ProjectionType.ALL)), + new LocalSecondaryIndex() + .withIndexName(indexFooCopyName) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(multipleIndexRangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withProjection(new Projection().withProjectionType(ProjectionType.ALL)), + new LocalSecondaryIndex() + .withIndexName(indexBarCopyName) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH), + new KeySchemaElement() + .withAttributeName(multipleIndexRangeKeyAttributeName) + .withKeyType(KeyType.RANGE)) + .withProjection(new Projection().withProjectionType(ProjectionType.ALL))) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(keyName) + .withAttributeType(ScalarAttributeType.N), + new AttributeDefinition() + .withAttributeName(rangeKeyAttributeName) + .withAttributeType(ScalarAttributeType.N), + new AttributeDefinition() + .withAttributeName(indexFooRangeKeyAttributeName) + .withAttributeType(ScalarAttributeType.N), + new AttributeDefinition() + .withAttributeName(indexBarRangeKeyAttributeName) + .withAttributeType(ScalarAttributeType.N), + new AttributeDefinition() + .withAttributeName(multipleIndexRangeKeyAttributeName) + .withAttributeType(ScalarAttributeType.N)); + createTableRequest.setProvisionedThroughput( + new ProvisionedThroughput().withReadCapacityUnits(10L).withWriteCapacityUnits(5L)); + + if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { + TableUtils.waitUntilActive(dynamo, TABLE_WITH_INDEX_RANGE_ATTRIBUTE); } - - public static void waitForTableToBecomeDeleted(AmazonDynamoDB dynamo, String tableName) { - log.info(() -> "Waiting for " + tableName + " to become Deleted..."); - long startTime = System.currentTimeMillis(); - long endTime = startTime + (60_000); - while (System.currentTimeMillis() < endTime) { - try { - Thread.sleep(5_000); - } catch (Exception e) { - // Ignored or expected. - } - try { - DescribeTableRequest request = new DescribeTableRequest(tableName); - TableDescription table = dynamo.describeTable(request).getTable(); - - log.info(() -> " - current state: " + table.getTableStatus()); - if (table.getTableStatus() == "DELETING") { - continue; - } - } catch (AmazonDynamoDBException exception) { - if (exception.getErrorCode().equalsIgnoreCase("ResourceNotFoundException")) { - log.info(() -> "successfully deleted"); - return; - } - } + } + + protected static void waitForTableToBecomeDeleted(String tableName) { + waitForTableToBecomeDeleted(dynamo, tableName); + } + + public static void waitForTableToBecomeDeleted(AmazonDynamoDB dynamo, String tableName) { + log.info(() -> "Waiting for " + tableName + " to become Deleted..."); + long startTime = System.currentTimeMillis(); + long endTime = startTime + (60_000); + while (System.currentTimeMillis() < endTime) { + try { + Thread.sleep(5_000); + } catch (Exception e) { + // Ignored or expected. + } + try { + DescribeTableRequest request = new DescribeTableRequest(tableName); + TableDescription table = dynamo.describeTable(request).getTable(); + + log.info(() -> " - current state: " + table.getTableStatus()); + if (table.getTableStatus() == "DELETING") { + continue; } - - throw new RuntimeException("Table " + tableName + " never went deleted"); + } catch (AmazonDynamoDBException exception) { + if (exception.getErrorCode().equalsIgnoreCase("ResourceNotFoundException")) { + log.info(() -> "successfully deleted"); + return; + } + } } - public static void main(String[] args) throws Exception { - setUp(); - deleteCryptoIntegrationTestTables(); - } + throw new RuntimeException("Table " + tableName + " never went deleted"); + } + + public static void main(String[] args) throws Exception { + setUp(); + deleteCryptoIntegrationTestTables(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBMapperCryptoIntegrationTestBase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBMapperCryptoIntegrationTestBase.java index 4cb0c5bf..596085e4 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBMapperCryptoIntegrationTestBase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBMapperCryptoIntegrationTestBase.java @@ -1,11 +1,11 @@ /* * Copyright 2015 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. @@ -13,28 +13,26 @@ package com.amazonaws.services.dynamodbv2.mapper.integration; import com.amazonaws.services.dynamodbv2.mapper.encryption.BinaryAttributeByteBufferTestClass; - import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; - public class DynamoDBMapperCryptoIntegrationTestBase extends DynamoDBCryptoIntegrationTestBase { - - public static void setUpMapperTestBase() { - DynamoDBCryptoIntegrationTestBase.setUpTestBase(); - } - /* - * Utility methods - */ - protected static BinaryAttributeByteBufferTestClass getUniqueByteBufferObject(int contentLength) { - BinaryAttributeByteBufferTestClass obj = new BinaryAttributeByteBufferTestClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBinaryAttribute(ByteBuffer.wrap(generateByteArray(contentLength))); - Set byteBufferSet = new HashSet(); - byteBufferSet.add(ByteBuffer.wrap(generateByteArray(contentLength))); - obj.setBinarySetAttribute(byteBufferSet); - return obj; - } + public static void setUpMapperTestBase() { + DynamoDBCryptoIntegrationTestBase.setUpTestBase(); + } + + /* + * Utility methods + */ + protected static BinaryAttributeByteBufferTestClass getUniqueByteBufferObject(int contentLength) { + BinaryAttributeByteBufferTestClass obj = new BinaryAttributeByteBufferTestClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setBinaryAttribute(ByteBuffer.wrap(generateByteArray(contentLength))); + Set byteBufferSet = new HashSet(); + byteBufferSet.add(ByteBuffer.wrap(generateByteArray(contentLength))); + obj.setBinarySetAttribute(byteBufferSet); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBTestBase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBTestBase.java index 9bf7160c..cbe594c5 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBTestBase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/DynamoDBTestBase.java @@ -12,80 +12,81 @@ */ package com.amazonaws.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.fail; +import static org.testng.AssertJUnit.assertTrue; + import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; - import java.math.BigDecimal; import java.util.Collection; import java.util.HashSet; import java.util.Set; -import static org.testng.Assert.fail; -import static org.testng.AssertJUnit.assertTrue; - public class DynamoDBTestBase { - protected static AmazonDynamoDB dynamo; + protected static AmazonDynamoDB dynamo; + + public static void setUpTestBase() { + dynamo = DynamoDBEmbedded.create(); + } - public static void setUpTestBase() { - dynamo = DynamoDBEmbedded.create(); + public static AmazonDynamoDB getClient() { + if (dynamo == null) { + setUpTestBase(); } + return dynamo; + } - public static AmazonDynamoDB getClient() { - if (dynamo == null) { - setUpTestBase(); - } - return dynamo; + protected static Set toSet(T... array) { + Set set = new HashSet(); + for (T t : array) { + set.add(t); } + return set; + } - protected static Set toSet(T... array) { - Set set = new HashSet(); - for (T t : array) { - set.add(t); - } - return set; + protected static void assertSetsEqual( + Collection expected, Collection given) { + Set givenCopy = new HashSet(); + givenCopy.addAll(given); + for (T e : expected) { + if (!givenCopy.remove(e)) { + fail("Expected element not found: " + e); + } } - protected static void assertSetsEqual(Collection expected, Collection given) { - Set givenCopy = new HashSet(); - givenCopy.addAll(given); - for (T e : expected) { - if (!givenCopy.remove(e)) { - fail("Expected element not found: " + e); - } - } + assertTrue("Unexpected elements found: " + givenCopy, givenCopy.isEmpty()); + } - assertTrue("Unexpected elements found: " + givenCopy, givenCopy.isEmpty()); + protected static void assertNumericSetsEquals( + Set expected, Collection given) { + Set givenCopy = new HashSet(); + for (String s : given) { + BigDecimal bd = new BigDecimal(s); + givenCopy.add(bd.setScale(0)); } - protected static void assertNumericSetsEquals(Set expected, Collection given) { - Set givenCopy = new HashSet(); - for (String s : given) { - BigDecimal bd = new BigDecimal(s); - givenCopy.add(bd.setScale(0)); - } + Set expectedCopy = new HashSet(); + for (Number n : expected) { + BigDecimal bd = new BigDecimal(n.toString()); + expectedCopy.add(bd.setScale(0)); + } - Set expectedCopy = new HashSet(); - for (Number n : expected) { - BigDecimal bd = new BigDecimal(n.toString()); - expectedCopy.add(bd.setScale(0)); - } + assertSetsEqual(expectedCopy, givenCopy); + } - assertSetsEqual(expectedCopy, givenCopy); - } - protected static Set toSet(Collection collection) { - Set set = new HashSet(); - for (T t : collection) { - set.add(t); - } - return set; + protected static Set toSet(Collection collection) { + Set set = new HashSet(); + for (T t : collection) { + set.add(t); } + return set; + } - protected static byte[] generateByteArray(int length) { - byte[] bytes = new byte[length]; - for (int i = 0; i < length; i++) { - bytes[i] = (byte) (i % Byte.MAX_VALUE); - } - return bytes; + protected static byte[] generateByteArray(int length) { + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) { + bytes[i] = (byte) (i % Byte.MAX_VALUE); } - + return bytes; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ExceptionHandlingITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ExceptionHandlingITCase.java index 35745de4..745adda6 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ExceptionHandlingITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ExceptionHandlingITCase.java @@ -1,11 +1,11 @@ /* * Copyright 2015 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. @@ -22,534 +22,525 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.Test; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.testng.annotations.Test; - -/** - * Tests of exception handling - */ +/** Tests of exception handling */ public class ExceptionHandlingITCase extends DynamoDBMapperCryptoIntegrationTestBase { - public static class NoTableAnnotation { + public static class NoTableAnnotation { + + private String key; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testNoTableAnnotation() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(new NoTableAnnotation()); + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testNoTableAnnotationLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.load(NoTableAnnotation.class, "abc"); + } - private String key; + @DynamoDBTable(tableName = TABLE_NAME) + public static class NoDefaultConstructor { - @DynamoDBHashKey - public String getKey() { - return key; - } + private String key; + private String attribute; - public void setKey(String key) { - this.key = key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + public String getAttribute() { + return attribute; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testNoTableAnnotation() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(new NoTableAnnotation()); + public void setAttribute(String attribute) { + this.attribute = attribute; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testNoTableAnnotationLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.load(NoTableAnnotation.class, "abc"); + public NoDefaultConstructor(String key, String attribute) { + super(); + this.key = key; + this.attribute = attribute; } + } - @DynamoDBTable(tableName = TABLE_NAME) - public static class NoDefaultConstructor { + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testNoDefaultConstructor() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + NoDefaultConstructor obj = new NoDefaultConstructor("" + startKey++, "abc"); + util.save(obj); + util.load(NoDefaultConstructor.class, obj.getKey()); + } - private String key; - private String attribute; + @DynamoDBTable(tableName = TABLE_NAME) + public static class NoKeyGetterDefined { - @DynamoDBHashKey - public String getKey() { - return key; - } + @SuppressWarnings("unused") + private String key; + } - public void setKey(String key) { - this.key = key; - } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testNoHashKeyGetter() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(new NoKeyGetterDefined()); + } - public String getAttribute() { - return attribute; - } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testNoHashKeyGetterLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.load(NoKeyGetterDefined.class, "abc"); + } - public void setAttribute(String attribute) { - this.attribute = attribute; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class PrivateKeyGetter { - public NoDefaultConstructor(String key, String attribute) { - super(); - this.key = key; - this.attribute = attribute; - } + private String key; + + @SuppressWarnings("unused") + @DynamoDBHashKey + public String getKey() { + return key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testNoDefaultConstructor() { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - NoDefaultConstructor obj = new NoDefaultConstructor("" + startKey++, "abc"); - util.save(obj); - util.load(NoDefaultConstructor.class, obj.getKey()); + @SuppressWarnings("unused") + private void setKey(String key) { + this.key = key; } + } - @DynamoDBTable(tableName = TABLE_NAME) - public static class NoKeyGetterDefined { + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testPrivateKeyGetter() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(new PrivateKeyGetter()); + } - @SuppressWarnings("unused") - private String key; - } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testPrivateKeyGetterLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.load(PrivateKeyGetter.class, "abc"); + } + + @DynamoDBTable(tableName = TABLE_NAME) + public static class PrivateKeySetter { - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testNoHashKeyGetter() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(new NoKeyGetterDefined()); + private String key; + + @DynamoDBHashKey + @DynamoDBAutoGeneratedKey + public String getKey() { + return key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testNoHashKeyGetterLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.load(NoKeyGetterDefined.class, "abc"); + @SuppressWarnings("unused") + private void setKey(String key) { + this.key = key; } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testPrivateKeySetter() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(new PrivateKeySetter()); + } + + /* + * To trigger this error, we need for a service object to be present, so + * we'll insert one manually. + */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testPrivateKeySetterLoad() throws Exception { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withS("abc")); + dynamo.putItem(new PutItemRequest().withTableName(TABLE_NAME).withItem(attr)); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.load(PrivateKeySetter.class, "abc"); + } - @DynamoDBTable(tableName = TABLE_NAME) - public static class PrivateKeyGetter { + @DynamoDBTable(tableName = TABLE_NAME) + public static class PrivateSetter { - private String key; + private String key; + private String StringProperty; - @SuppressWarnings("unused") - @DynamoDBHashKey - public String getKey() { - return key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } - @SuppressWarnings("unused") - private void setKey(String key) { - this.key = key; - } + public void setKey(String key) { + this.key = key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testPrivateKeyGetter() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(new PrivateKeyGetter()); + public String getStringProperty() { + return StringProperty; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testPrivateKeyGetterLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.load(PrivateKeyGetter.class, "abc"); + private void setStringProperty(String stringProperty) { + StringProperty = stringProperty; } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testPrivateSetterLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + PrivateSetter object = new PrivateSetter(); + object.setStringProperty("value"); + util.save(object); + util.load(PrivateSetter.class, object.getKey()); + } - @DynamoDBTable(tableName = TABLE_NAME) - public static class PrivateKeySetter { + @DynamoDBTable(tableName = TABLE_NAME) + public static class OverloadedSetter { - private String key; + private String key; + private String attribute; - @DynamoDBHashKey - @DynamoDBAutoGeneratedKey - public String getKey() { - return key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } - @SuppressWarnings("unused") - private void setKey(String key) { - this.key = key; - } + public void setKey(String key) { + this.key = key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testPrivateKeySetter() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(new PrivateKeySetter()); + public String getAttribute() { + return attribute; } - /* - * To trigger this error, we need for a service object to be present, so - * we'll insert one manually. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testPrivateKeySetterLoad() throws Exception { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withS("abc")); - dynamo.putItem(new PutItemRequest().withTableName(TABLE_NAME).withItem(attr)); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.load(PrivateKeySetter.class, "abc"); + public void setAttribute(String attribute, String unused) { + this.attribute = attribute; } + } - @DynamoDBTable(tableName = TABLE_NAME) - public static class PrivateSetter { + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testOverloadedSetter() { + OverloadedSetter obj = new OverloadedSetter(); + obj.setKey("" + startKey++); + obj.setAttribute("abc", "123"); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); - private String key; - private String StringProperty; + mapper.load(OverloadedSetter.class, obj.getKey()); + } - @DynamoDBHashKey - public String getKey() { - return key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class WrongTypeForSetter { - public void setKey(String key) { - this.key = key; - } + private String key; + private String attribute; - public String getStringProperty() { - return StringProperty; - } + @DynamoDBHashKey + public String getKey() { + return key; + } - private void setStringProperty(String stringProperty) { - StringProperty = stringProperty; - } + public void setKey(String key) { + this.key = key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testPrivateSetterLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - PrivateSetter object = new PrivateSetter(); - object.setStringProperty("value"); - util.save(object); - util.load(PrivateSetter.class, object.getKey()); + public String getAttribute() { + return attribute; } - @DynamoDBTable(tableName = TABLE_NAME) - public static class OverloadedSetter { + public void setAttribute(Integer attribute) { + this.attribute = String.valueOf(attribute); + } + } - private String key; - private String attribute; + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testWrongTypeForSetter() { + WrongTypeForSetter obj = new WrongTypeForSetter(); + obj.setKey("" + startKey++); + obj.setAttribute(123); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); - @DynamoDBHashKey - public String getKey() { - return key; - } + mapper.load(WrongTypeForSetter.class, obj.getKey()); + } - public void setKey(String key) { - this.key = key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class NumericFields { - public String getAttribute() { - return attribute; - } + private String key; + private Integer integerProperty; - public void setAttribute(String attribute, String unused) { - this.attribute = attribute; - } + @DynamoDBHashKey + public String getKey() { + return key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testOverloadedSetter() { - OverloadedSetter obj = new OverloadedSetter(); - obj.setKey("" + startKey++); - obj.setAttribute("abc", "123"); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); + public void setKey(String key) { + this.key = key; + } - mapper.load(OverloadedSetter.class, obj.getKey()); + public Integer getIntegerProperty() { + return integerProperty; } - @DynamoDBTable(tableName = TABLE_NAME) - public static class WrongTypeForSetter { + public void setIntegerProperty(Integer integerProperty) { + this.integerProperty = integerProperty; + } + } - private String key; - private String attribute; + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testWrongDataType() { + Map attr = new HashMap(); + attr.put("integerProperty", new AttributeValue().withS("abc")); + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + dynamo.putItem(new PutItemRequest().withTableName(TABLE_NAME).withItem(attr)); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.load(NumericFields.class, attr.get(KEY_NAME).getS()); + } - @DynamoDBHashKey - public String getKey() { - return key; - } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testWrongDataType2() { + Map attr = new HashMap(); + attr.put("integerProperty", new AttributeValue().withNS("1", "2", "3")); + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + dynamo.putItem(new PutItemRequest().withTableName(TABLE_NAME).withItem(attr)); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.load(NumericFields.class, attr.get(KEY_NAME).getS()); + } - public void setKey(String key) { - this.key = key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class ComplexType { - public String getAttribute() { - return attribute; - } + public String key; + public ComplexType type; - public void setAttribute(Integer attribute) { - this.attribute = String.valueOf(attribute); - } + public ComplexType(String key, ComplexType type) { + super(); + this.key = key; + this.type = type; + } + @DynamoDBHashKey + public String getKey() { + return key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testWrongTypeForSetter() { - WrongTypeForSetter obj = new WrongTypeForSetter(); - obj.setKey("" + startKey++); - obj.setAttribute(123); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); + public void setKey(String key) { + this.key = key; + } - mapper.load(WrongTypeForSetter.class, obj.getKey()); + public ComplexType getType() { + return type; } - @DynamoDBTable(tableName = TABLE_NAME) - public static class NumericFields { + public void setType(ComplexType type) { + this.type = type; + } + } - private String key; - private Integer integerProperty; + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testComplexTypeFailure() { + ComplexType complexType = + new ComplexType("" + startKey++, new ComplexType("" + startKey++, null)); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(complexType); + } - @DynamoDBHashKey - public String getKey() { - return key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class ComplexHashKeyType { - public void setKey(String key) { - this.key = key; - } + private ComplexType key; + private String attribute; - public Integer getIntegerProperty() { - return integerProperty; - } + @DynamoDBHashKey + public ComplexType getKey() { + return key; + } - public void setIntegerProperty(Integer integerProperty) { - this.integerProperty = integerProperty; - } + public void setKey(ComplexType key) { + this.key = key; + } + public String getAttribute() { + return attribute; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testWrongDataType() { - Map attr = new HashMap(); - attr.put("integerProperty", new AttributeValue().withS("abc")); - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - dynamo.putItem(new PutItemRequest().withTableName(TABLE_NAME).withItem(attr)); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.load(NumericFields.class, attr.get(KEY_NAME).getS()); + public void setAttribute(String attribute) { + this.attribute = attribute; } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testUnsupportedHashKeyType() { + ComplexType complexType = + new ComplexType("" + startKey++, new ComplexType("" + startKey++, null)); + ComplexHashKeyType obj = new ComplexHashKeyType(); + obj.setKey(complexType); + obj.setAttribute("abc"); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(obj); + } + + @DynamoDBTable(tableName = TABLE_NAME) + public static class NonSetCollectionType { + + private String key; + private List badlyMapped; - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testWrongDataType2() { - Map attr = new HashMap(); - attr.put("integerProperty", new AttributeValue().withNS("1", "2", "3")); - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - dynamo.putItem(new PutItemRequest().withTableName(TABLE_NAME).withItem(attr)); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.load(NumericFields.class, attr.get(KEY_NAME).getS()); + @DynamoDBHashKey + public String getKey() { + return key; } - @DynamoDBTable(tableName = TABLE_NAME) - public static class ComplexType { + public void setKey(String key) { + this.key = key; + } - public String key; - public ComplexType type; + public List getBadlyMapped() { + return badlyMapped; + } - public ComplexType(String key, ComplexType type) { - super(); - this.key = key; - this.type = type; - } + public void setBadlyMapped(List badlyMapped) { + this.badlyMapped = badlyMapped; + } + } - @DynamoDBHashKey - public String getKey() { - return key; - } + @Test + public void testNonSetCollection() { + NonSetCollectionType obj = new NonSetCollectionType(); + obj.setKey("" + startKey++); + obj.setBadlyMapped(new ArrayList()); + obj.getBadlyMapped().add("abc"); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); + } - public void setKey(String key) { - this.key = key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class FractionalVersionAttribute { - public ComplexType getType() { - return type; - } + private String key; + private Double version; - public void setType(ComplexType type) { - this.type = type; - } + @DynamoDBHashKey + public String getKey() { + return key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testComplexTypeFailure() { - ComplexType complexType = new ComplexType("" + startKey++, new ComplexType("" + startKey++, null)); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(complexType); + public void setKey(String key) { + this.key = key; } - @DynamoDBTable(tableName = TABLE_NAME) - public static class ComplexHashKeyType { + @DynamoDBVersionAttribute + public Double getVersion() { + return version; + } - private ComplexType key; - private String attribute; + public void setVersion(Double version) { + this.version = version; + } + } - @DynamoDBHashKey - public ComplexType getKey() { - return key; - } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testFractionalVersionAttribute() { + FractionalVersionAttribute obj = new FractionalVersionAttribute(); + obj.setKey("" + startKey++); + obj.setVersion(0d); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); + } - public void setKey(ComplexType key) { - this.key = key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class AutoGeneratedIntegerKey { - public String getAttribute() { - return attribute; - } + private Integer key; + private String value; - public void setAttribute(String attribute) { - this.attribute = attribute; - } + @DynamoDBHashKey + @DynamoDBAutoGeneratedKey + public Integer getKey() { + return key; } - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testUnsupportedHashKeyType() { - ComplexType complexType = new ComplexType("" + startKey++, new ComplexType("" + startKey++, null)); - ComplexHashKeyType obj = new ComplexHashKeyType(); - obj.setKey(complexType); - obj.setAttribute("abc"); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(obj); + public void setKey(Integer key) { + this.key = key; } - @DynamoDBTable(tableName = TABLE_NAME) - public static class NonSetCollectionType { + public String getValue() { + return value; + } - private String key; - private List badlyMapped; + public void setValue(String value) { + this.value = value; + } + } - @DynamoDBHashKey - public String getKey() { - return key; - } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testAutoGeneratedIntegerHashKey() { + AutoGeneratedIntegerKey obj = new AutoGeneratedIntegerKey(); + obj.setValue("fdgfdsgf"); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); + } - public void setKey(String key) { - this.key = key; - } + @DynamoDBTable(tableName = TABLE_NAME) + public static class AutoGeneratedIntegerRangeKey { - public List getBadlyMapped() { - return badlyMapped; - } + private String key; + private Integer rangekey; + private String value; - public void setBadlyMapped(List badlyMapped) { - this.badlyMapped = badlyMapped; - } + @DynamoDBHashKey + public String getKey() { + return key; } - @Test - public void testNonSetCollection() { - NonSetCollectionType obj = new NonSetCollectionType(); - obj.setKey("" + startKey++); - obj.setBadlyMapped(new ArrayList()); - obj.getBadlyMapped().add("abc"); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); - } - - @DynamoDBTable(tableName = TABLE_NAME) - public static class FractionalVersionAttribute { - - private String key; - private Double version; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } + public void setKey(String key) { + this.key = key; + } - @DynamoDBVersionAttribute - public Double getVersion() { - return version; - } + @DynamoDBAutoGeneratedKey + @DynamoDBRangeKey + public Integer getRangekey() { + return rangekey; + } - public void setVersion(Double version) { - this.version = version; - } + public void setRangekey(Integer rangekey) { + this.rangekey = rangekey; + } + public String getValue() { + return value; } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testFractionalVersionAttribute() { - FractionalVersionAttribute obj = new FractionalVersionAttribute(); - obj.setKey("" + startKey++); - obj.setVersion(0d); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); - } - - @DynamoDBTable(tableName = TABLE_NAME) - public static class AutoGeneratedIntegerKey { - - private Integer key; - private String value; - - @DynamoDBHashKey - @DynamoDBAutoGeneratedKey - public Integer getKey() { - return key; - } - - public void setKey(Integer key) { - this.key = key; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testAutoGeneratedIntegerHashKey() { - AutoGeneratedIntegerKey obj = new AutoGeneratedIntegerKey(); - obj.setValue("fdgfdsgf"); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); - } - - @DynamoDBTable(tableName = TABLE_NAME) - public static class AutoGeneratedIntegerRangeKey { - - private String key; - private Integer rangekey; - private String value; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAutoGeneratedKey - @DynamoDBRangeKey - public Integer getRangekey() { - return rangekey; - } - - public void setRangekey(Integer rangekey) { - this.rangekey = rangekey; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testAutoGeneratedIntegerRangeKey() { - AutoGeneratedIntegerRangeKey obj = new AutoGeneratedIntegerRangeKey(); - obj.setKey("Bldadsfa"); - obj.setValue("fdgfdsgf"); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); + + public void setValue(String value) { + this.value = value; } + } + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testAutoGeneratedIntegerRangeKey() { + AutoGeneratedIntegerRangeKey obj = new AutoGeneratedIntegerRangeKey(); + obj.setKey("Bldadsfa"); + obj.setValue("fdgfdsgf"); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/HashKeyOnlyTableWithGSIITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/HashKeyOnlyTableWithGSIITCase.java index 7ad7d04b..70522f6e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/HashKeyOnlyTableWithGSIITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/HashKeyOnlyTableWithGSIITCase.java @@ -12,6 +12,9 @@ */ package com.amazonaws.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexRangeKey; @@ -34,131 +37,117 @@ import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.util.TableUtils; +import java.util.ArrayList; +import java.util.List; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import java.util.ArrayList; -import java.util.List; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - - /** - * Integration test for GSI support with a table that has no primary range key - * (only a primary hash key). + * Integration test for GSI support with a table that has no primary range key (only a primary hash + * key). */ -public class HashKeyOnlyTableWithGSIITCase extends - DynamoDBMapperCryptoIntegrationTestBase { - - public static final String HASH_KEY_ONLY_TABLE_NAME = "no-primary-range-key-gsi-test-crypto"; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBTestBase.setUpTestBase(); - List keySchema = new ArrayList(); - keySchema.add(new KeySchemaElement("id", KeyType.HASH)); - - CreateTableRequest req = new CreateTableRequest( - HASH_KEY_ONLY_TABLE_NAME, keySchema) - .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)) - .withAttributeDefinitions( - new AttributeDefinition("id", ScalarAttributeType.S), - new AttributeDefinition("status", ScalarAttributeType.S), - new AttributeDefinition("ts", ScalarAttributeType.S)) - .withGlobalSecondaryIndexes( - new GlobalSecondaryIndex() - .withProvisionedThroughput( - new ProvisionedThroughput(10L, 10L)) - .withIndexName("statusAndCreation") - .withKeySchema( - new KeySchemaElement("status", - KeyType.HASH), - new KeySchemaElement("ts", - KeyType.RANGE)) - .withProjection( - new Projection() - .withProjectionType(ProjectionType.ALL))); - - TableUtils.createTableIfNotExists(dynamo, req); - TableUtils.waitUntilActive(dynamo, HASH_KEY_ONLY_TABLE_NAME); +public class HashKeyOnlyTableWithGSIITCase extends DynamoDBMapperCryptoIntegrationTestBase { + + public static final String HASH_KEY_ONLY_TABLE_NAME = "no-primary-range-key-gsi-test-crypto"; + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBTestBase.setUpTestBase(); + List keySchema = new ArrayList(); + keySchema.add(new KeySchemaElement("id", KeyType.HASH)); + + CreateTableRequest req = + new CreateTableRequest(HASH_KEY_ONLY_TABLE_NAME, keySchema) + .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)) + .withAttributeDefinitions( + new AttributeDefinition("id", ScalarAttributeType.S), + new AttributeDefinition("status", ScalarAttributeType.S), + new AttributeDefinition("ts", ScalarAttributeType.S)) + .withGlobalSecondaryIndexes( + new GlobalSecondaryIndex() + .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)) + .withIndexName("statusAndCreation") + .withKeySchema( + new KeySchemaElement("status", KeyType.HASH), + new KeySchemaElement("ts", KeyType.RANGE)) + .withProjection(new Projection().withProjectionType(ProjectionType.ALL))); + + TableUtils.createTableIfNotExists(dynamo, req); + TableUtils.waitUntilActive(dynamo, HASH_KEY_ONLY_TABLE_NAME); + } + + @AfterClass + public static void tearDown() throws Exception { + dynamo.deleteTable(HASH_KEY_ONLY_TABLE_NAME); + } + + @DynamoDBTable(tableName = HASH_KEY_ONLY_TABLE_NAME) + public static class User { + private String id; + private String status; + private String ts; + + @DynamoDBHashKey + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @DoNotEncrypt + @DynamoDBIndexHashKey(globalSecondaryIndexName = "statusAndCreation") + public String getStatus() { + return status; } - @AfterClass - public static void tearDown() throws Exception { - dynamo.deleteTable(HASH_KEY_ONLY_TABLE_NAME); + public void setStatus(String status) { + this.status = status; } - @DynamoDBTable(tableName = HASH_KEY_ONLY_TABLE_NAME) - public static class User { - private String id; - private String status; - private String ts; - - @DynamoDBHashKey - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @DoNotEncrypt - @DynamoDBIndexHashKey(globalSecondaryIndexName = "statusAndCreation") - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - @DoNotEncrypt - @DynamoDBIndexRangeKey(globalSecondaryIndexName = "statusAndCreation") - public String getTs() { - return ts; - } - - public void setTs(String ts) { - this.ts = ts; - } + @DoNotEncrypt + @DynamoDBIndexRangeKey(globalSecondaryIndexName = "statusAndCreation") + public String getTs() { + return ts; } - /** - * Tests that we can query using the hash/range GSI on our hash-key only - * table. - */ - @Test - public void testGSIQuery() throws Exception { - DynamoDBMapper mapper = TestDynamoDBMapperFactory - .createDynamoDBMapper(dynamo); - String status = "foo-status"; - - User user = new User(); - user.setId("123"); - user.setStatus(status); - user.setTs("321"); - mapper.save(user); - - DynamoDBQueryExpression expr = new DynamoDBQueryExpression() - .withIndexName("statusAndCreation") - .withLimit(100) - .withConsistentRead(false) - .withHashKeyValues(user) - .withRangeKeyCondition( - "ts", - new Condition() - .withComparisonOperator(ComparisonOperator.GT) - .withAttributeValueList(new AttributeValue("100"))); - - PaginatedQueryList query = mapper.query(User.class, expr); - int size = query.size(); - if (DEBUG) - System.err.println("size=" + size); - assertTrue(1 == size); - assertEquals(status, query.get(0).getStatus()); + public void setTs(String ts) { + this.ts = ts; } - private static final boolean DEBUG = false; + } + + /** Tests that we can query using the hash/range GSI on our hash-key only table. */ + @Test + public void testGSIQuery() throws Exception { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + String status = "foo-status"; + + User user = new User(); + user.setId("123"); + user.setStatus(status); + user.setTs("321"); + mapper.save(user); + + DynamoDBQueryExpression expr = + new DynamoDBQueryExpression() + .withIndexName("statusAndCreation") + .withLimit(100) + .withConsistentRead(false) + .withHashKeyValues(user) + .withRangeKeyCondition( + "ts", + new Condition() + .withComparisonOperator(ComparisonOperator.GT) + .withAttributeValueList(new AttributeValue("100"))); + + PaginatedQueryList query = mapper.query(User.class, expr); + int size = query.size(); + if (DEBUG) System.err.println("size=" + size); + assertTrue(1 == size); + assertEquals(status, query.get(0).getStatus()); + } + + private static final boolean DEBUG = false; } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/IndexRangeKeyAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/IndexRangeKeyAttributesITCase.java index c47a8175..f880bf8a 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/IndexRangeKeyAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/IndexRangeKeyAttributesITCase.java @@ -1,17 +1,23 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression; @@ -24,9 +30,6 @@ import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import com.amazonaws.services.dynamodbv2.model.Condition; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; @@ -34,386 +37,390 @@ import java.util.List; import java.util.Map; import java.util.UUID; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; /** - * Tests that index range keys are properly handled as common attribute - * when items are loaded, saved/updated by using primary key. - * Also tests using index range keys for queries. + * Tests that index range keys are properly handled as common attribute when items are loaded, + * saved/updated by using primary key. Also tests using index range keys for queries. */ public class IndexRangeKeyAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static DynamoDBMapper mapper; - private static final String RANGE_KEY = "rangeKey"; - private static final String INDEX_FOO_RANGE_KEY = "indexFooRangeKey"; - private static final String INDEX_BAR_RANGE_KEY = "indexBarRangeKey"; - private static final String MULTIPLE_INDEX_RANGE_KEY = "multipleIndexRangeKey"; - private static final String FOO_ATTRIBUTE = "fooAttribute"; - private static final String BAR_ATTRIBUTE = "barAttribute"; - private static final String VERSION_ATTRIBUTE = "version"; + private static DynamoDBMapper mapper; + private static final String RANGE_KEY = "rangeKey"; + private static final String INDEX_FOO_RANGE_KEY = "indexFooRangeKey"; + private static final String INDEX_BAR_RANGE_KEY = "indexBarRangeKey"; + private static final String MULTIPLE_INDEX_RANGE_KEY = "multipleIndexRangeKey"; + private static final String FOO_ATTRIBUTE = "fooAttribute"; + private static final String BAR_ATTRIBUTE = "barAttribute"; + private static final String VERSION_ATTRIBUTE = "version"; - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; - private static final List> attrs = new LinkedList>(); - private static final List hashKeyValues = new LinkedList(); - private static final int totalHash = 5; - private static final int rangePerHash = 64; - private static final int indexFooRangeStep = 2; - private static final int indexBarRangeStep = 4; - private static final int multipleIndexRangeStep = 8; - // Test data - static { - for ( int i = 0; i < totalHash; i++ ) { - long hashKeyValue = startKey++; - hashKeyValues.add(hashKeyValue); - for (int j = 0; j< rangePerHash; j++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withN("" + hashKeyValue)); - attr.put(RANGE_KEY, new AttributeValue().withN("" + j)); - if ( j % indexFooRangeStep == 0) - attr.put(INDEX_FOO_RANGE_KEY, new AttributeValue().withN("" + j)); - if ( j % indexBarRangeStep == 0) - attr.put(INDEX_BAR_RANGE_KEY, new AttributeValue().withN("" + j)); - if ( j % multipleIndexRangeStep == 0) - attr.put(MULTIPLE_INDEX_RANGE_KEY, new AttributeValue().withN("" + j)); - attr.put(FOO_ATTRIBUTE, new AttributeValue().withS(UUID.randomUUID().toString())); - attr.put(BAR_ATTRIBUTE, new AttributeValue().withS(UUID.randomUUID().toString())); - attr.put(VERSION_ATTRIBUTE, new AttributeValue().withN("1")); + private static final List> attrs = + new LinkedList>(); + private static final List hashKeyValues = new LinkedList(); + private static final int totalHash = 5; + private static final int rangePerHash = 64; + private static final int indexFooRangeStep = 2; + private static final int indexBarRangeStep = 4; + private static final int multipleIndexRangeStep = 8; + // Test data + static { + for (int i = 0; i < totalHash; i++) { + long hashKeyValue = startKey++; + hashKeyValues.add(hashKeyValue); + for (int j = 0; j < rangePerHash; j++) { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withN("" + hashKeyValue)); + attr.put(RANGE_KEY, new AttributeValue().withN("" + j)); + if (j % indexFooRangeStep == 0) + attr.put(INDEX_FOO_RANGE_KEY, new AttributeValue().withN("" + j)); + if (j % indexBarRangeStep == 0) + attr.put(INDEX_BAR_RANGE_KEY, new AttributeValue().withN("" + j)); + if (j % multipleIndexRangeStep == 0) + attr.put(MULTIPLE_INDEX_RANGE_KEY, new AttributeValue().withN("" + j)); + attr.put(FOO_ATTRIBUTE, new AttributeValue().withS(UUID.randomUUID().toString())); + attr.put(BAR_ATTRIBUTE, new AttributeValue().withS(UUID.randomUUID().toString())); + attr.put(VERSION_ATTRIBUTE, new AttributeValue().withN("1")); - attrs.add(attr); - } - } - }; + attrs.add(attr); + } + } + } + ; - @BeforeClass - public static void setUp() throws Exception { - boolean recreateTable = false; - setUpTableWithIndexRangeAttribute(recreateTable); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() + @BeforeClass + public static void setUp() throws Exception { + boolean recreateTable = false; + setUpTableWithIndexRangeAttribute(recreateTable); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder() .withHashKeyName(KEY_NAME) .withRangeKeyName(RANGE_KEY) .withTableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE) .build(); - // Insert the data - for ( Map attr : attrs ) { - attr = encryptor.encryptAllFieldsExcept(attr, context, - KEY_NAME, - RANGE_KEY, - INDEX_FOO_RANGE_KEY, - INDEX_BAR_RANGE_KEY, - MULTIPLE_INDEX_RANGE_KEY, - VERSION_ATTRIBUTE); - dynamo.putItem(new PutItemRequest(TABLE_WITH_INDEX_RANGE_ATTRIBUTE, attr)); - } - mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + // Insert the data + for (Map attr : attrs) { + attr = + encryptor.encryptAllFieldsExcept( + attr, + context, + KEY_NAME, + RANGE_KEY, + INDEX_FOO_RANGE_KEY, + INDEX_BAR_RANGE_KEY, + MULTIPLE_INDEX_RANGE_KEY, + VERSION_ATTRIBUTE); + dynamo.putItem(new PutItemRequest(TABLE_WITH_INDEX_RANGE_ATTRIBUTE, attr)); } - - /** - * Tests that attribute annotated with @DynamoDBIndexRangeKey is properly set in the loaded object. - */ - @Test - public void testLoad() throws Exception { - for ( Map attr : attrs ) { - IndexRangeKeyTestClass x = mapper.load(newIndexRangeKey( - Long.parseLong(attr.get(KEY_NAME).getN()), - Double.parseDouble(attr.get(RANGE_KEY).getN()) - )); + mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + } - // Convert all numbers to the most inclusive type for easy - // comparison - assertEquals(new BigDecimal(x.getKey()), new BigDecimal(attr.get(KEY_NAME).getN())); - assertEquals(new BigDecimal(x.getRangeKey()), new BigDecimal(attr.get(RANGE_KEY).getN())); - if (null == attr.get(INDEX_FOO_RANGE_KEY)) - assertNull(x.getIndexFooRangeKeyWithFakeName()); - else - assertEquals(new BigDecimal(x.getIndexFooRangeKeyWithFakeName()), new BigDecimal(attr.get(INDEX_FOO_RANGE_KEY).getN())); - if (null == attr.get(INDEX_BAR_RANGE_KEY)) - assertNull(x.getIndexBarRangeKey()); - else - assertEquals(new BigDecimal(x.getIndexBarRangeKey()), new BigDecimal(attr.get(INDEX_BAR_RANGE_KEY).getN())); - assertEquals(new BigDecimal(x.getVersion()), new BigDecimal(attr.get(VERSION_ATTRIBUTE).getN())); - assertEquals(x.getFooAttribute(), attr.get(FOO_ATTRIBUTE).getS()); - assertEquals(x.getBarAttribute(), attr.get(BAR_ATTRIBUTE).getS()); + /** + * Tests that attribute annotated with @DynamoDBIndexRangeKey is properly set in the loaded + * object. + */ + @Test + public void testLoad() throws Exception { + for (Map attr : attrs) { + IndexRangeKeyTestClass x = + mapper.load( + newIndexRangeKey( + Long.parseLong(attr.get(KEY_NAME).getN()), + Double.parseDouble(attr.get(RANGE_KEY).getN()))); - } - } - - private IndexRangeKeyTestClass newIndexRangeKey(long hashKey, double rangeKey) { - IndexRangeKeyTestClass obj = new IndexRangeKeyTestClass(); - obj.setKey(hashKey); - obj.setRangeKey(rangeKey); - return obj; + // Convert all numbers to the most inclusive type for easy + // comparison + assertEquals(new BigDecimal(x.getKey()), new BigDecimal(attr.get(KEY_NAME).getN())); + assertEquals(new BigDecimal(x.getRangeKey()), new BigDecimal(attr.get(RANGE_KEY).getN())); + if (null == attr.get(INDEX_FOO_RANGE_KEY)) assertNull(x.getIndexFooRangeKeyWithFakeName()); + else + assertEquals( + new BigDecimal(x.getIndexFooRangeKeyWithFakeName()), + new BigDecimal(attr.get(INDEX_FOO_RANGE_KEY).getN())); + if (null == attr.get(INDEX_BAR_RANGE_KEY)) assertNull(x.getIndexBarRangeKey()); + else + assertEquals( + new BigDecimal(x.getIndexBarRangeKey()), + new BigDecimal(attr.get(INDEX_BAR_RANGE_KEY).getN())); + assertEquals( + new BigDecimal(x.getVersion()), new BigDecimal(attr.get(VERSION_ATTRIBUTE).getN())); + assertEquals(x.getFooAttribute(), attr.get(FOO_ATTRIBUTE).getS()); + assertEquals(x.getBarAttribute(), attr.get(BAR_ATTRIBUTE).getS()); } + } - /** - * Tests that attribute annotated with @DynamoDBIndexRangeKey is properly saved. - */ - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - IndexRangeKeyTestClass obj = getUniqueObject(); - objs.add(obj); - } + private IndexRangeKeyTestClass newIndexRangeKey(long hashKey, double rangeKey) { + IndexRangeKeyTestClass obj = new IndexRangeKeyTestClass(); + obj.setKey(hashKey); + obj.setRangeKey(rangeKey); + return obj; + } - for (IndexRangeKeyTestClass obj : objs) { - mapper.save(obj); - } + /** Tests that attribute annotated with @DynamoDBIndexRangeKey is properly saved. */ + @Test + public void testSave() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + IndexRangeKeyTestClass obj = getUniqueObject(); + objs.add(obj); + } - for (IndexRangeKeyTestClass obj : objs) { - IndexRangeKeyTestClass loaded = mapper.load(IndexRangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(obj, loaded); - } + for (IndexRangeKeyTestClass obj : objs) { + mapper.save(obj); } - /** - * Tests that version attribute is still working as expected. - */ - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - IndexRangeKeyTestClass obj = getUniqueObject(); - objs.add(obj); - } + for (IndexRangeKeyTestClass obj : objs) { + IndexRangeKeyTestClass loaded = + mapper.load(IndexRangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); + assertEquals(obj, loaded); + } + } - for (IndexRangeKeyTestClass obj : objs) { - mapper.save(obj); - } + /** Tests that version attribute is still working as expected. */ + @Test + public void testUpdate() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + IndexRangeKeyTestClass obj = getUniqueObject(); + objs.add(obj); + } - for ( IndexRangeKeyTestClass obj : objs ) { - IndexRangeKeyTestClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - replacement.setRangeKey(obj.getRangeKey()); - replacement.setVersion(obj.getVersion()); - mapper.save(replacement); + for (IndexRangeKeyTestClass obj : objs) { + mapper.save(obj); + } - IndexRangeKeyTestClass loadedObject = mapper.load(IndexRangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(replacement, loadedObject); + for (IndexRangeKeyTestClass obj : objs) { + IndexRangeKeyTestClass replacement = getUniqueObject(); + replacement.setKey(obj.getKey()); + replacement.setRangeKey(obj.getRangeKey()); + replacement.setVersion(obj.getVersion()); + mapper.save(replacement); - // If we try to update the old version, we should get an error - replacement.setVersion(replacement.getVersion() - 1); - try { - mapper.save(replacement); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - } - } - - /** - * Tests making queries on local secondary index - */ - @Test - public void testQueryWithIndexRangekey() { - int indexFooRangePerHash = rangePerHash / indexFooRangeStep; - int indexBarRangePerHash = rangePerHash / indexBarRangeStep; - for ( long hashKeyValue : hashKeyValues ) { - IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); - hashKeyItem.setKey(hashKeyValue); - - /** - * Query items by primary range key - */ - List result = mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString()))); - assertTrue(rangePerHash == result.size()); - // check that all attributes are retrieved - for (IndexRangeKeyTestClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNotNull(itemInFooIndex.getBarAttribute()); - } - - /** - * Query items on index_foo - */ - result = mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(INDEX_FOO_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString()))); - assertTrue(indexFooRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyTestClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNotNull(itemInFooIndex.getBarAttribute()); - } - - /** - * Query items on index_bar - */ - result = mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(INDEX_BAR_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString()))); - assertTrue(indexBarRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyTestClass itemInBarIndex : result) { - assertNotNull(itemInBarIndex.getFooAttribute()); - assertNotNull(itemInBarIndex.getBarAttribute()); - } - } + IndexRangeKeyTestClass loadedObject = + mapper.load(IndexRangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); + assertEquals(replacement, loadedObject); + + // If we try to update the old version, we should get an error + replacement.setVersion(replacement.getVersion() - 1); + try { + mapper.save(replacement); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } } - - /** - * Tests the exception when user specifies an invalid range key name in the query. - */ - @Test - public void testInvalidRangeKeyNameException() { - IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); - hashKeyItem.setKey(0); - try { - mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition("some_range_key", - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString()))); - fail("some_range_key is not a valid range key name."); - } catch (DynamoDBMappingException e) { - System.out.println(e.getMessage()); - } catch (Exception e) { - fail("Should trigger an DynamoDBMappingException."); - } + } + + /** Tests making queries on local secondary index */ + @Test + public void testQueryWithIndexRangekey() { + int indexFooRangePerHash = rangePerHash / indexFooRangeStep; + int indexBarRangePerHash = rangePerHash / indexBarRangeStep; + for (long hashKeyValue : hashKeyValues) { + IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); + hashKeyItem.setKey(hashKeyValue); + + /** Query items by primary range key */ + List result = + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString()))); + assertTrue(rangePerHash == result.size()); + // check that all attributes are retrieved + for (IndexRangeKeyTestClass itemInFooIndex : result) { + assertNotNull(itemInFooIndex.getFooAttribute()); + assertNotNull(itemInFooIndex.getBarAttribute()); + } + + /** Query items on index_foo */ + result = + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + INDEX_FOO_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString()))); + assertTrue(indexFooRangePerHash == result.size()); + // check that only the projected attributes are retrieved + for (IndexRangeKeyTestClass itemInFooIndex : result) { + assertNotNull(itemInFooIndex.getFooAttribute()); + assertNotNull(itemInFooIndex.getBarAttribute()); + } + + /** Query items on index_bar */ + result = + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + INDEX_BAR_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString()))); + assertTrue(indexBarRangePerHash == result.size()); + // check that only the projected attributes are retrieved + for (IndexRangeKeyTestClass itemInBarIndex : result) { + assertNotNull(itemInBarIndex.getFooAttribute()); + assertNotNull(itemInBarIndex.getBarAttribute()); + } } - - /** - * Tests the exception when user specifies an invalid index name in the query. - */ - @Test - public void testInvalidIndexNameException() { - IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); - hashKeyItem.setKey(0); - try { - mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(INDEX_BAR_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString())) - .withIndexName("some_index")); - fail("some_index is not a valid index name."); - } catch (IllegalArgumentException iae) { - System.out.println(iae.getMessage()); - } catch (Exception e) { - fail("Should trigger an IllegalArgumentException."); - } + } + + /** Tests the exception when user specifies an invalid range key name in the query. */ + @Test + public void testInvalidRangeKeyNameException() { + IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); + hashKeyItem.setKey(0); + try { + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + "some_range_key", + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString()))); + fail("some_range_key is not a valid range key name."); + } catch (DynamoDBMappingException e) { + System.out.println(e.getMessage()); + } catch (Exception e) { + fail("Should trigger an DynamoDBMappingException."); } - - /** - * Tests making queries by using range key that is shared by multiple indexes. - */ - @Test - public void testQueryWithRangeKeyForMultipleIndexes() { - int multipleIndexRangePerHash = rangePerHash / multipleIndexRangeStep; - for ( long hashKeyValue : hashKeyValues ) { - IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); - hashKeyItem.setKey(hashKeyValue); - - /** - * Query items by a range key that is shared by multiple indexes - */ - List result = mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString())) - .withIndexName("index_foo_copy")); - assertTrue(multipleIndexRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyTestClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNotNull(itemInFooIndex.getBarAttribute()); - } - result = mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString())) - .withIndexName("index_bar_copy")); - assertTrue(multipleIndexRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyTestClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNotNull(itemInFooIndex.getBarAttribute()); - } - - /** - * Exception when user doesn't specify which index to use - */ - try { - mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString()))); - fail("No index name is specified when query with a range key shared by multiple indexes"); - } catch (IllegalArgumentException iae) { - System.out.println(iae.getMessage()); - } catch (Exception e) { - fail("Should trigger an IllegalArgumentException."); - } - - /** - * Exception when user uses an invalid index name - */ - try { - mapper.query(IndexRangeKeyTestClass.class, - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - new Condition() - .withAttributeValueList(new AttributeValue().withN("0")) - .withComparisonOperator(ComparisonOperator.GE.toString())) - .withIndexName("index_foo")); - fail("index_foo is not annotated as part of the localSecondaryIndexNames in " + - "the @DynamoDBIndexRangeKey annotation of multipleIndexRangeKey"); - } catch (IllegalArgumentException iae) { - System.out.println(iae.getMessage()); - } catch (Exception e) { - fail("Should trigger an IllegalArgumentException."); - } - } - + } + + /** Tests the exception when user specifies an invalid index name in the query. */ + @Test + public void testInvalidIndexNameException() { + IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); + hashKeyItem.setKey(0); + try { + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + INDEX_BAR_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString())) + .withIndexName("some_index")); + fail("some_index is not a valid index name."); + } catch (IllegalArgumentException iae) { + System.out.println(iae.getMessage()); + } catch (Exception e) { + fail("Should trigger an IllegalArgumentException."); } - + } - private IndexRangeKeyTestClass getUniqueObject() { - IndexRangeKeyTestClass obj = new IndexRangeKeyTestClass(); - obj.setKey(startKey++); - obj.setRangeKey((double) start++); - obj.setIndexFooRangeKeyWithFakeName((double) start++); - obj.setIndexBarRangeKey((double) start++); - obj.setFooAttribute("" + startKey++); - obj.setBarAttribute("" + startKey++); - return obj; + /** Tests making queries by using range key that is shared by multiple indexes. */ + @Test + public void testQueryWithRangeKeyForMultipleIndexes() { + int multipleIndexRangePerHash = rangePerHash / multipleIndexRangeStep; + for (long hashKeyValue : hashKeyValues) { + IndexRangeKeyTestClass hashKeyItem = new IndexRangeKeyTestClass(); + hashKeyItem.setKey(hashKeyValue); + + /** Query items by a range key that is shared by multiple indexes */ + List result = + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + MULTIPLE_INDEX_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString())) + .withIndexName("index_foo_copy")); + assertTrue(multipleIndexRangePerHash == result.size()); + // check that only the projected attributes are retrieved + for (IndexRangeKeyTestClass itemInFooIndex : result) { + assertNotNull(itemInFooIndex.getFooAttribute()); + assertNotNull(itemInFooIndex.getBarAttribute()); + } + result = + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + MULTIPLE_INDEX_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString())) + .withIndexName("index_bar_copy")); + assertTrue(multipleIndexRangePerHash == result.size()); + // check that only the projected attributes are retrieved + for (IndexRangeKeyTestClass itemInFooIndex : result) { + assertNotNull(itemInFooIndex.getFooAttribute()); + assertNotNull(itemInFooIndex.getBarAttribute()); + } + + /** Exception when user doesn't specify which index to use */ + try { + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + MULTIPLE_INDEX_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString()))); + fail("No index name is specified when query with a range key shared by multiple indexes"); + } catch (IllegalArgumentException iae) { + System.out.println(iae.getMessage()); + } catch (Exception e) { + fail("Should trigger an IllegalArgumentException."); + } + + /** Exception when user uses an invalid index name */ + try { + mapper.query( + IndexRangeKeyTestClass.class, + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyItem) + .withRangeKeyCondition( + MULTIPLE_INDEX_RANGE_KEY, + new Condition() + .withAttributeValueList(new AttributeValue().withN("0")) + .withComparisonOperator(ComparisonOperator.GE.toString())) + .withIndexName("index_foo")); + fail( + "index_foo is not annotated as part of the localSecondaryIndexNames in " + + "the @DynamoDBIndexRangeKey annotation of multipleIndexRangeKey"); + } catch (IllegalArgumentException iae) { + System.out.println(iae.getMessage()); + } catch (Exception e) { + fail("Should trigger an IllegalArgumentException."); + } } - + } + + private IndexRangeKeyTestClass getUniqueObject() { + IndexRangeKeyTestClass obj = new IndexRangeKeyTestClass(); + obj.setKey(startKey++); + obj.setRangeKey((double) start++); + obj.setIndexFooRangeKeyWithFakeName((double) start++); + obj.setIndexBarRangeKey((double) start++); + obj.setFooAttribute("" + startKey++); + obj.setBarAttribute("" + startKey++); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/InheritanceITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/InheritanceITCase.java index 65d1f46d..8b24c284 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/InheritanceITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/InheritanceITCase.java @@ -1,300 +1,272 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; -import org.testng.annotations.Test; - import java.util.ArrayList; import java.util.List; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +/** Tests inheritance behavior in DynamoDB mapper. */ +public class InheritanceITCase extends DynamoDBMapperCryptoIntegrationTestBase { + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static class BaseClass { -/** - * Tests inheritance behavior in DynamoDB mapper. - */ -public class InheritanceITCase extends DynamoDBMapperCryptoIntegrationTestBase { + protected String key; + protected String normalStringAttribute; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAttribute + public String getNormalStringAttribute() { + return normalStringAttribute; + } + + public void setNormalStringAttribute(String normalStringAttribute) { + this.normalStringAttribute = normalStringAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = + prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + BaseClass other = (BaseClass) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (normalStringAttribute == null) { + if (other.normalStringAttribute != null) return false; + } else if (!normalStringAttribute.equals(other.normalStringAttribute)) return false; + return true; + } + } + + public static class SubClass extends BaseClass { + + private String subField; + + public String getSubField() { + return subField; + } + + public void setSubField(String subField) { + this.subField = subField; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((subField == null) ? 0 : subField.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + SubClass other = (SubClass) obj; + if (subField == null) { + if (other.subField != null) return false; + } else if (!subField.equals(other.subField)) return false; + return true; + } + } + + public static class SubSubClass extends SubClass { + + private String subSubField; - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static class BaseClass { - - protected String key; - protected String normalStringAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAttribute - public String getNormalStringAttribute() { - return normalStringAttribute; - } - - public void setNormalStringAttribute(String normalStringAttribute) { - this.normalStringAttribute = normalStringAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - BaseClass other = (BaseClass) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( normalStringAttribute == null ) { - if ( other.normalStringAttribute != null ) - return false; - } else if ( !normalStringAttribute.equals(other.normalStringAttribute) ) - return false; - return true; - } + public String getSubSubField() { + return subSubField; } - public static class SubClass extends BaseClass { - - private String subField; - - public String getSubField() { - return subField; - } - - public void setSubField(String subField) { - this.subField = subField; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((subField == null) ? 0 : subField.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - SubClass other = (SubClass) obj; - if ( subField == null ) { - if ( other.subField != null ) - return false; - } else if ( !subField.equals(other.subField) ) - return false; - return true; - } + public void setSubSubField(String subSubField) { + this.subSubField = subSubField; + } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((subSubField == null) ? 0 : subSubField.hashCode()); + return result; } - public static class SubSubClass extends SubClass { - - private String subSubField; - - public String getSubSubField() { - return subSubField; - } - - public void setSubSubField(String subSubField) { - this.subSubField = subSubField; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((subSubField == null) ? 0 : subSubField.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - SubSubClass other = (SubSubClass) obj; - if ( subSubField == null ) { - if ( other.subSubField != null ) - return false; - } else if ( !subSubField.equals(other.subSubField) ) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + SubSubClass other = (SubSubClass) obj; + if (subSubField == null) { + if (other.subSubField != null) return false; + } else if (!subSubField.equals(other.subSubField)) return false; + return true; + } + } + + @Test + public void testSubClass() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + SubClass obj = getUniqueObject(new SubClass()); + obj.setSubField("" + startKey++); + objs.add(obj); } - @Test - public void testSubClass() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - SubClass obj = getUniqueObject(new SubClass()); - obj.setSubField("" + startKey++); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( Object obj : objs ) { - util.save(obj); - assertEquals(util.load(SubClass.class, ((SubClass)obj).getKey()), obj); - } + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (Object obj : objs) { + util.save(obj); + assertEquals(util.load(SubClass.class, ((SubClass) obj).getKey()), obj); + } + } + + @Test + public void testSubSubClass() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + SubSubClass obj = getUniqueObject(new SubSubClass()); + obj.setSubField("" + startKey++); + obj.setSubSubField("" + startKey++); + objs.add(obj); } - @Test - public void testSubSubClass() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - SubSubClass obj = getUniqueObject(new SubSubClass()); - obj.setSubField("" + startKey++); - obj.setSubSubField("" + startKey++); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( SubSubClass obj : objs ) { - util.save(obj); - assertEquals(util.load(SubSubClass.class, obj.getKey()), obj); - } + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (SubSubClass obj : objs) { + util.save(obj); + assertEquals(util.load(SubSubClass.class, obj.getKey()), obj); } + } - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static interface Interface { + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static interface Interface { - @DynamoDBHashKey - public String getKey(); + @DynamoDBHashKey + public String getKey(); - public void setKey(String key); + public void setKey(String key); - @DynamoDBAttribute - public String getAttribute(); + @DynamoDBAttribute + public String getAttribute(); - public void setAttribute(String attribute); + public void setAttribute(String attribute); + } + + public static class Implementation implements Interface { + + private String key; + private String attribute; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getAttribute() { + return attribute; + } + + public void setAttribute(String attribute) { + this.attribute = attribute; } - public static class Implementation implements Interface { - - private String key; - private String attribute; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getAttribute() { - return attribute; - } - - public void setAttribute(String attribute) { - this.attribute = attribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((attribute == null) ? 0 : attribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - Implementation other = (Implementation) obj; - if ( attribute == null ) { - if ( other.attribute != null ) - return false; - } else if ( !attribute.equals(other.attribute) ) - return false; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - return true; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((attribute == null) ? 0 : attribute.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testImplementation() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - Implementation obj = new Implementation(); - obj.setKey("" + startKey++); - obj.setAttribute("" + startKey++); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( Interface obj : objs ) { - util.save(obj); - assertEquals(util.load(Implementation.class, obj.getKey()), obj); - } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Implementation other = (Implementation) obj; + if (attribute == null) { + if (other.attribute != null) return false; + } else if (!attribute.equals(other.attribute)) return false; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + return true; + } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testImplementation() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + Implementation obj = new Implementation(); + obj.setKey("" + startKey++); + obj.setAttribute("" + startKey++); + objs.add(obj); } - private T getUniqueObject(T obj) { - obj.setKey("" + startKey++); - obj.setNormalStringAttribute("" + startKey++); - return obj; + // Saving new objects with a null version field should populate it + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (Interface obj : objs) { + util.save(obj); + assertEquals(util.load(Implementation.class, obj.getKey()), obj); } + } + + private T getUniqueObject(T obj) { + obj.setKey("" + startKey++); + obj.setNormalStringAttribute("" + startKey++); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/KeyOnlyPutITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/KeyOnlyPutITCase.java index 8bb01798..5d58341e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/KeyOnlyPutITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/KeyOnlyPutITCase.java @@ -1,11 +1,11 @@ /* * Copyright 2015 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. @@ -16,66 +16,59 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; - public class KeyOnlyPutITCase extends DynamoDBCryptoIntegrationTestBase { - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static class HashAndAttribute { - - protected String key; - protected String normalStringAttribute; + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static class HashAndAttribute { - @DynamoDBHashKey - public String getKey() { - return key; - } + protected String key; + protected String normalStringAttribute; - public void setKey(String key) { - this.key = key; - } + @DynamoDBHashKey + public String getKey() { + return key; + } - @DynamoDBAttribute - public String getNormalStringAttribute() { - return normalStringAttribute; - } + public void setKey(String key) { + this.key = key; + } - public void setNormalStringAttribute(String normalStringAttribute) { - this.normalStringAttribute = normalStringAttribute; - } + @DynamoDBAttribute + public String getNormalStringAttribute() { + return normalStringAttribute; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); - return result; - } + public void setNormalStringAttribute(String normalStringAttribute) { + this.normalStringAttribute = normalStringAttribute; + } - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - HashAndAttribute other = (HashAndAttribute) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( normalStringAttribute == null ) { - if ( other.normalStringAttribute != null ) - return false; - } else if ( !normalStringAttribute.equals(other.normalStringAttribute) ) - return false; - return true; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = + prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); + return result; } - private T getUniqueObject(T obj) { - obj.setKey("" + startKey++); - return obj; + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + HashAndAttribute other = (HashAndAttribute) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (normalStringAttribute == null) { + if (other.normalStringAttribute != null) return false; + } else if (!normalStringAttribute.equals(other.normalStringAttribute)) return false; + return true; } + } + + private T getUniqueObject(T obj) { + obj.setKey("" + startKey++); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperLoadingStrategyConfigITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperLoadingStrategyConfigITCase.java index 3a8dae57..75da14ce 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperLoadingStrategyConfigITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperLoadingStrategyConfigITCase.java @@ -12,6 +12,11 @@ */ package com.amazonaws.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.ConsistentReads; @@ -24,300 +29,335 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import com.amazonaws.services.dynamodbv2.model.Condition; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - - -/** - * Integration tests for PaginationLoadingStrategy configuration - */ +/** Integration tests for PaginationLoadingStrategy configuration */ public class MapperLoadingStrategyConfigITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static long hashKey = System.currentTimeMillis(); - private static int PAGE_SIZE = 5; - private static int PARALLEL_SEGMENT = 3; - private static int OBJECTS_NUM = 50; - private static int RESULTS_NUM = OBJECTS_NUM - 2; // condition: rangeKey > 1.0 - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - createTestData(); + private static long hashKey = System.currentTimeMillis(); + private static int PAGE_SIZE = 5; + private static int PARALLEL_SEGMENT = 3; + private static int OBJECTS_NUM = 50; + private static int RESULTS_NUM = OBJECTS_NUM - 2; // condition: rangeKey > 1.0 + + @BeforeClass + public static void setUp() throws Exception { + setUpTableWithRangeAttribute(); + createTestData(); + } + + @Test + public void testLazyLoading() { + // Get all the paginated lists using the tested loading strategy + PaginatedList queryList = + getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); + PaginatedList scanList = + getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); + PaginatedList parallelScanList = + getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); + + // check that only at most one page of results are loaded up to this point + assertTrue(getLoadedResultsNumber(queryList) <= PAGE_SIZE); + assertTrue(getLoadedResultsNumber(scanList) <= PAGE_SIZE); + assertTrue(getLoadedResultsNumber(parallelScanList) <= PAGE_SIZE * PARALLEL_SEGMENT); + + testAllPaginatedListOperations(queryList); + testAllPaginatedListOperations(scanList); + testAllPaginatedListOperations(parallelScanList); + + // Re-construct the paginated lists and test the iterator behavior + queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); + scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); + parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); + + testPaginatedListIterator(queryList); + testPaginatedListIterator(scanList); + testPaginatedListIterator(parallelScanList); + } + + @Test + public void testEagerLoading() { + // Get all the paginated lists using the tested loading strategy + PaginatedList queryList = + getTestPaginatedQueryList(PaginationLoadingStrategy.EAGER_LOADING); + PaginatedList scanList = + getTestPaginatedScanList(PaginationLoadingStrategy.EAGER_LOADING); + PaginatedList parallelScanList = + getTestPaginatedParallelScanList(PaginationLoadingStrategy.EAGER_LOADING); + + // check that all results have been loaded + assertTrue(RESULTS_NUM == getLoadedResultsNumber(queryList)); + assertTrue(RESULTS_NUM == getLoadedResultsNumber(scanList)); + assertTrue(RESULTS_NUM == getLoadedResultsNumber(parallelScanList)); + + testAllPaginatedListOperations(queryList); + testAllPaginatedListOperations(scanList); + testAllPaginatedListOperations(parallelScanList); + + // Re-construct the paginated lists and test the iterator behavior + queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); + scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); + parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); + + testPaginatedListIterator(queryList); + testPaginatedListIterator(scanList); + testPaginatedListIterator(parallelScanList); + } + + @Test + public void testIterationOnly() { + // Get all the paginated lists using the tested loading strategy + PaginatedList queryList = + getTestPaginatedQueryList(PaginationLoadingStrategy.ITERATION_ONLY); + PaginatedList scanList = + getTestPaginatedScanList(PaginationLoadingStrategy.ITERATION_ONLY); + PaginatedList parallelScanList = + getTestPaginatedParallelScanList(PaginationLoadingStrategy.ITERATION_ONLY); + + // check that only at most one page of results are loaded up to this point + assertTrue(getLoadedResultsNumber(queryList) <= PAGE_SIZE); + assertTrue(getLoadedResultsNumber(scanList) <= PAGE_SIZE); + assertTrue(getLoadedResultsNumber(parallelScanList) <= PAGE_SIZE * PARALLEL_SEGMENT); + + testIterationOnlyPaginatedListOperations(queryList); + testIterationOnlyPaginatedListOperations(scanList); + testIterationOnlyPaginatedListOperations(parallelScanList); + } + + private static void createTestData() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + List objs = new ArrayList(); + for (int i = 0; i < OBJECTS_NUM; i++) { + RangeKeyTestClass obj = new RangeKeyTestClass(); + obj.setKey(hashKey); + obj.setRangeKey(i); + objs.add(obj); } - @Test - public void testLazyLoading() { - // Get all the paginated lists using the tested loading strategy - PaginatedList queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); - PaginatedList scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); - PaginatedList parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); - - // check that only at most one page of results are loaded up to this point - assertTrue(getLoadedResultsNumber(queryList) <= PAGE_SIZE); - assertTrue(getLoadedResultsNumber(scanList) <= PAGE_SIZE); - assertTrue(getLoadedResultsNumber(parallelScanList) <= PAGE_SIZE * PARALLEL_SEGMENT); + mapper.batchSave(objs); + } + + private static PaginatedList getTestPaginatedQueryList( + PaginationLoadingStrategy paginationLoadingStrategy) { + DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); + DynamoDBMapper mapper = new DynamoDBMapper(dynamo, mapperConfig); + + // Construct the query expression for the tested hash-key value and any range-key value greater + // that 1.0 + RangeKeyTestClass keyObject = new RangeKeyTestClass(); + keyObject.setKey(hashKey); + DynamoDBQueryExpression queryExpression = + new DynamoDBQueryExpression().withHashKeyValues(keyObject); + queryExpression + .withRangeKeyCondition( + "rangeKey", + new Condition() + .withComparisonOperator(ComparisonOperator.GT.toString()) + .withAttributeValueList(new AttributeValue().withN("1.0"))) + .withLimit(PAGE_SIZE); + + return mapper.query( + RangeKeyTestClass.class, + queryExpression, + new DynamoDBMapperConfig(paginationLoadingStrategy)); + } + + private static PaginatedList getTestPaginatedScanList( + PaginationLoadingStrategy paginationLoadingStrategy) { + DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); + DynamoDBMapper mapper = new DynamoDBMapper(dynamo, mapperConfig); + + // Construct the scan expression with the exact same conditions + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); + scanExpression.addFilterCondition( + "key", + new Condition() + .withComparisonOperator(ComparisonOperator.EQ) + .withAttributeValueList(new AttributeValue().withN(Long.toString(hashKey)))); + scanExpression.addFilterCondition( + "rangeKey", + new Condition() + .withComparisonOperator(ComparisonOperator.GT) + .withAttributeValueList(new AttributeValue().withN("1.0"))); + scanExpression.setLimit(PAGE_SIZE); + + return mapper.scan( + RangeKeyTestClass.class, + scanExpression, + new DynamoDBMapperConfig(paginationLoadingStrategy)); + } + + private static PaginatedList getTestPaginatedParallelScanList( + PaginationLoadingStrategy paginationLoadingStrategy) { + DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); + DynamoDBMapper mapper = new DynamoDBMapper(dynamo, mapperConfig); + + // Construct the scan expression with the exact same conditions + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); + scanExpression.addFilterCondition( + "key", + new Condition() + .withComparisonOperator(ComparisonOperator.EQ) + .withAttributeValueList(new AttributeValue().withN(Long.toString(hashKey)))); + scanExpression.addFilterCondition( + "rangeKey", + new Condition() + .withComparisonOperator(ComparisonOperator.GT) + .withAttributeValueList(new AttributeValue().withN("1.0"))); + scanExpression.setLimit(PAGE_SIZE); + + return mapper.parallelScan( + RangeKeyTestClass.class, + scanExpression, + PARALLEL_SEGMENT, + new DynamoDBMapperConfig(paginationLoadingStrategy)); + } + + private static void testAllPaginatedListOperations(PaginatedList list) { + + // (1) isEmpty() + assertFalse(list.isEmpty()); + + // (2) get(int n) + assertNotNull(list.get(RESULTS_NUM / 2)); + + // (3) contains(Object org0) + RangeKeyTestClass obj = new RangeKeyTestClass(); + obj.setKey(hashKey); + obj.setRangeKey(0); + assertFalse(list.contains(obj)); + obj.setRangeKey(2); + assertTrue(list.contains(obj)); + + // (4) subList(int org0, int arg1) + List subList = list.subList(0, RESULTS_NUM); + assertTrue(RESULTS_NUM == subList.size()); + try { + list.subList(0, RESULTS_NUM + 1); + fail("IndexOutOfBoundsException is IndexOutOfBoundsException but not thrown"); + } catch (IndexOutOfBoundsException e) { + } - testAllPaginatedListOperations(queryList); - testAllPaginatedListOperations(scanList); - testAllPaginatedListOperations(parallelScanList); + // (5) indexOf(Object org0) + assertTrue(list.indexOf(obj) < RESULTS_NUM); - // Re-construct the paginated lists and test the iterator behavior - queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); - scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); - parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); + // (6) loadAllResults() + list.loadAllResults(); - testPaginatedListIterator(queryList); - testPaginatedListIterator(scanList); - testPaginatedListIterator(parallelScanList); + // (7) size() + assertTrue(RESULTS_NUM == list.size()); + } + private static void testPaginatedListIterator(PaginatedList list) { + for (RangeKeyTestClass item : list) { + assertTrue(hashKey == item.getKey()); + assertTrue(item.getRangeKey() < OBJECTS_NUM); } - @Test - public void testEagerLoading() { - // Get all the paginated lists using the tested loading strategy - PaginatedList queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.EAGER_LOADING); - PaginatedList scanList = getTestPaginatedScanList(PaginationLoadingStrategy.EAGER_LOADING); - PaginatedList parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.EAGER_LOADING); - - // check that all results have been loaded - assertTrue(RESULTS_NUM == getLoadedResultsNumber(queryList)); - assertTrue(RESULTS_NUM == getLoadedResultsNumber(scanList)); - assertTrue(RESULTS_NUM == getLoadedResultsNumber(parallelScanList)); - - testAllPaginatedListOperations(queryList); - testAllPaginatedListOperations(scanList); - testAllPaginatedListOperations(parallelScanList); - - // Re-construct the paginated lists and test the iterator behavior - queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); - scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); - parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); - - testPaginatedListIterator(queryList); - testPaginatedListIterator(scanList); - testPaginatedListIterator(parallelScanList); + // make sure the list could be iterated again + for (RangeKeyTestClass item : list) { + assertTrue(hashKey == item.getKey()); + assertTrue(item.getRangeKey() < OBJECTS_NUM); } + } - @Test - public void testIterationOnly() { - // Get all the paginated lists using the tested loading strategy - PaginatedList queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.ITERATION_ONLY); - PaginatedList scanList = getTestPaginatedScanList(PaginationLoadingStrategy.ITERATION_ONLY); - PaginatedList parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.ITERATION_ONLY); - - // check that only at most one page of results are loaded up to this point - assertTrue(getLoadedResultsNumber(queryList) <= PAGE_SIZE); - assertTrue(getLoadedResultsNumber(scanList) <= PAGE_SIZE); - assertTrue(getLoadedResultsNumber(parallelScanList) <= PAGE_SIZE * PARALLEL_SEGMENT); - - testIterationOnlyPaginatedListOperations(queryList); - testIterationOnlyPaginatedListOperations(scanList); - testIterationOnlyPaginatedListOperations(parallelScanList); - } + private static void testIterationOnlyPaginatedListOperations( + PaginatedList list) { - private static void createTestData() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + // Unsupported operations - List objs = new ArrayList(); - for ( int i = 0; i < OBJECTS_NUM; i++ ) { - RangeKeyTestClass obj = new RangeKeyTestClass(); - obj.setKey(hashKey); - obj.setRangeKey(i); - objs.add(obj); - } - - mapper.batchSave(objs); + // (1) isEmpty() + try { + list.isEmpty(); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { } - private static PaginatedList getTestPaginatedQueryList(PaginationLoadingStrategy paginationLoadingStrategy) { - DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); - DynamoDBMapper mapper = new DynamoDBMapper(dynamo, mapperConfig); - - // Construct the query expression for the tested hash-key value and any range-key value greater that 1.0 - RangeKeyTestClass keyObject = new RangeKeyTestClass(); - keyObject.setKey(hashKey); - DynamoDBQueryExpression queryExpression = new DynamoDBQueryExpression().withHashKeyValues(keyObject); - queryExpression.withRangeKeyCondition("rangeKey", - new Condition().withComparisonOperator(ComparisonOperator.GT.toString()).withAttributeValueList( - new AttributeValue().withN("1.0"))).withLimit(PAGE_SIZE); - - return mapper.query(RangeKeyTestClass.class, queryExpression, new DynamoDBMapperConfig(paginationLoadingStrategy)); + // (2) get(int n) + try { + list.get(RESULTS_NUM / 2); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { } - private static PaginatedList getTestPaginatedScanList(PaginationLoadingStrategy paginationLoadingStrategy) { - DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); - DynamoDBMapper mapper = new DynamoDBMapper(dynamo, mapperConfig); - - // Construct the scan expression with the exact same conditions - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); - scanExpression.addFilterCondition("key", - new Condition().withComparisonOperator(ComparisonOperator.EQ).withAttributeValueList( - new AttributeValue().withN(Long.toString(hashKey)))); - scanExpression.addFilterCondition("rangeKey", - new Condition().withComparisonOperator(ComparisonOperator.GT).withAttributeValueList( - new AttributeValue().withN("1.0"))); - scanExpression.setLimit(PAGE_SIZE); - - return mapper.scan(RangeKeyTestClass.class, scanExpression, new DynamoDBMapperConfig(paginationLoadingStrategy)); + // (3) contains(Object org0) + try { + list.contains(new RangeKeyTestClass()); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { } - private static PaginatedList getTestPaginatedParallelScanList(PaginationLoadingStrategy paginationLoadingStrategy) { - DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); - DynamoDBMapper mapper = new DynamoDBMapper(dynamo, mapperConfig); - - // Construct the scan expression with the exact same conditions - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); - scanExpression.addFilterCondition("key", - new Condition().withComparisonOperator(ComparisonOperator.EQ).withAttributeValueList( - new AttributeValue().withN(Long.toString(hashKey)))); - scanExpression.addFilterCondition("rangeKey", - new Condition().withComparisonOperator(ComparisonOperator.GT).withAttributeValueList( - new AttributeValue().withN("1.0"))); - scanExpression.setLimit(PAGE_SIZE); - - return mapper.parallelScan(RangeKeyTestClass.class, scanExpression, PARALLEL_SEGMENT, new DynamoDBMapperConfig(paginationLoadingStrategy)); + // (4) subList(int org0, int arg1) + try { + list.subList(0, RESULTS_NUM); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { } - private static void testAllPaginatedListOperations(PaginatedList list) { - - // (1) isEmpty() - assertFalse(list.isEmpty()); - - // (2) get(int n) - assertNotNull(list.get(RESULTS_NUM / 2)); - - // (3) contains(Object org0) - RangeKeyTestClass obj = new RangeKeyTestClass(); - obj.setKey(hashKey); - obj.setRangeKey(0); - assertFalse(list.contains(obj)); - obj.setRangeKey(2); - assertTrue(list.contains(obj)); - - // (4) subList(int org0, int arg1) - List subList = list.subList(0, RESULTS_NUM); - assertTrue(RESULTS_NUM == subList.size()); - try { - list.subList(0, RESULTS_NUM + 1); - fail("IndexOutOfBoundsException is IndexOutOfBoundsException but not thrown"); - } catch (IndexOutOfBoundsException e) {} - - // (5) indexOf(Object org0) - assertTrue(list.indexOf(obj) < RESULTS_NUM); - - // (6) loadAllResults() - list.loadAllResults(); - - // (7) size() - assertTrue(RESULTS_NUM == list.size()); - + // (5) indexOf(Object org0) + try { + list.indexOf(new RangeKeyTestClass()); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { } - private static void testPaginatedListIterator(PaginatedList list) { - for (RangeKeyTestClass item : list) { - assertTrue(hashKey == item.getKey()); - assertTrue(item.getRangeKey() < OBJECTS_NUM); - } - - // make sure the list could be iterated again - for (RangeKeyTestClass item : list) { - assertTrue(hashKey == item.getKey()); - assertTrue(item.getRangeKey() < OBJECTS_NUM); - } + // (6) loadAllResults() + try { + list.loadAllResults(); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { } - private static void testIterationOnlyPaginatedListOperations(PaginatedList list) { - - // Unsupported operations - - // (1) isEmpty() - try { - list.isEmpty(); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {} - - // (2) get(int n) - try { - list.get(RESULTS_NUM / 2); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {} - - // (3) contains(Object org0) - try { - list.contains(new RangeKeyTestClass()); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {} - - // (4) subList(int org0, int arg1) - try { - list.subList(0, RESULTS_NUM); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {} - - // (5) indexOf(Object org0) - try { - list.indexOf(new RangeKeyTestClass()); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {} - - // (6) loadAllResults() - try { - list.loadAllResults(); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {} - - // (7) size() - try { - list.size(); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) {}; - - // Could be iterated once - for (RangeKeyTestClass item : list) { - assertTrue(hashKey == item.getKey()); - assertTrue(item.getRangeKey() < OBJECTS_NUM); - // At most one page of results in memeory - assertTrue(getLoadedResultsNumber(list) <= PAGE_SIZE); - } - - // not twice - try { - for (@SuppressWarnings("unused") RangeKeyTestClass item : list) { - fail("UnsupportedOperationException expected but is not thrown"); - } - } catch (UnsupportedOperationException e) {} - + // (7) size() + try { + list.size(); + fail("UnsupportedOperationException expected but is not thrown"); + } catch (UnsupportedOperationException e) { + } + ; + + // Could be iterated once + for (RangeKeyTestClass item : list) { + assertTrue(hashKey == item.getKey()); + assertTrue(item.getRangeKey() < OBJECTS_NUM); + // At most one page of results in memeory + assertTrue(getLoadedResultsNumber(list) <= PAGE_SIZE); } - /** Use reflection to get the size of the private allResults field **/ - @SuppressWarnings("unchecked") - private static int getLoadedResultsNumber(PaginatedList list) { - Field privateAllResults = null; - try { - privateAllResults = list.getClass().getSuperclass().getDeclaredField("allResults"); - } catch (SecurityException e) { - fail(e.getMessage()); - } catch (NoSuchFieldException e) { - fail(e.getMessage()); - } - privateAllResults.setAccessible(true); - List allResults = null; - try { - allResults = (List) privateAllResults.get(list); - } catch (IllegalArgumentException e) { - fail(e.getMessage()); - } catch (IllegalAccessException e) { - fail(e.getMessage()); - } - return allResults.size(); + // not twice + try { + for (@SuppressWarnings("unused") RangeKeyTestClass item : list) { + fail("UnsupportedOperationException expected but is not thrown"); + } + } catch (UnsupportedOperationException e) { + } + } + + /** Use reflection to get the size of the private allResults field * */ + @SuppressWarnings("unchecked") + private static int getLoadedResultsNumber(PaginatedList list) { + Field privateAllResults = null; + try { + privateAllResults = list.getClass().getSuperclass().getDeclaredField("allResults"); + } catch (SecurityException e) { + fail(e.getMessage()); + } catch (NoSuchFieldException e) { + fail(e.getMessage()); + } + privateAllResults.setAccessible(true); + List allResults = null; + try { + allResults = (List) privateAllResults.get(list); + } catch (IllegalArgumentException e) { + fail(e.getMessage()); + } catch (IllegalAccessException e) { + fail(e.getMessage()); } + return allResults.size(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigCryptoIntegrationTestBase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigCryptoIntegrationTestBase.java index a7ebeb1c..16c3f004 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigCryptoIntegrationTestBase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigCryptoIntegrationTestBase.java @@ -12,6 +12,9 @@ */ package com.amazonaws.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; @@ -29,189 +32,183 @@ import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.model.TableDescription; import com.amazonaws.services.dynamodbv2.util.TableUtils; -import org.testng.annotations.BeforeClass; - import java.util.Set; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - +import org.testng.annotations.BeforeClass; public class MapperSaveConfigCryptoIntegrationTestBase extends DynamoDBCryptoIntegrationTestBase { - protected static DynamoDBMapper dynamoMapper; - - protected static final DynamoDBMapperConfig defaultConfig = new DynamoDBMapperConfig( - SaveBehavior.UPDATE); - protected static final DynamoDBMapperConfig updateSkipNullConfig = new DynamoDBMapperConfig( - SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - protected static final DynamoDBMapperConfig appendSetConfig = new DynamoDBMapperConfig( - SaveBehavior.APPEND_SET); - protected static final DynamoDBMapperConfig clobberConfig = new DynamoDBMapperConfig( - SaveBehavior.CLOBBER); - - protected static final String tableName = "aws-java-sdk-dynamodb-mapper-save-config-test-crypto"; - - protected static final String hashKeyName = "hashKey"; - - protected static final String rangeKeyName = "rangeKey"; - - protected static final String nonKeyAttributeName = "nonKeyAttribute"; - - protected static final String stringSetAttributeName = "stringSetAttribute"; - - /** Read capacity for the test table being created in Amazon DynamoDB. */ - protected static final Long READ_CAPACITY = 10L; - - /** Write capacity for the test table being created in Amazon DynamoDB. */ - protected static final Long WRITE_CAPACITY = 5L; - - /** Provisioned Throughput for the test table created in Amazon DynamoDB */ - protected static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = new ProvisionedThroughput() - .withReadCapacityUnits(READ_CAPACITY).withWriteCapacityUnits( - WRITE_CAPACITY); - - @BeforeClass - public static void setUp() throws Exception { - System.setProperty("sqlite4java.library.path", "target/test-lib"); - DynamoDBCryptoIntegrationTestBase.setUp(); - dynamoMapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(tableName) - .withKeySchema(new KeySchemaElement().withAttributeName(hashKeyName).withKeyType(KeyType.HASH)) - .withKeySchema(new KeySchemaElement().withAttributeName(rangeKeyName).withKeyType(KeyType.RANGE)) - .withAttributeDefinitions(new AttributeDefinition().withAttributeName(hashKeyName) - .withAttributeType(ScalarAttributeType.S)) - .withAttributeDefinitions(new AttributeDefinition().withAttributeName(rangeKeyName) - .withAttributeType(ScalarAttributeType.N)); - createTableRequest.setProvisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, tableName); - } + protected static DynamoDBMapper dynamoMapper; + + protected static final DynamoDBMapperConfig defaultConfig = + new DynamoDBMapperConfig(SaveBehavior.UPDATE); + protected static final DynamoDBMapperConfig updateSkipNullConfig = + new DynamoDBMapperConfig(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); + protected static final DynamoDBMapperConfig appendSetConfig = + new DynamoDBMapperConfig(SaveBehavior.APPEND_SET); + protected static final DynamoDBMapperConfig clobberConfig = + new DynamoDBMapperConfig(SaveBehavior.CLOBBER); + + protected static final String tableName = "aws-java-sdk-dynamodb-mapper-save-config-test-crypto"; + + protected static final String hashKeyName = "hashKey"; + + protected static final String rangeKeyName = "rangeKey"; + + protected static final String nonKeyAttributeName = "nonKeyAttribute"; + + protected static final String stringSetAttributeName = "stringSetAttribute"; + + /** Read capacity for the test table being created in Amazon DynamoDB. */ + protected static final Long READ_CAPACITY = 10L; + + /** Write capacity for the test table being created in Amazon DynamoDB. */ + protected static final Long WRITE_CAPACITY = 5L; + + /** Provisioned Throughput for the test table created in Amazon DynamoDB */ + protected static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = + new ProvisionedThroughput() + .withReadCapacityUnits(READ_CAPACITY) + .withWriteCapacityUnits(WRITE_CAPACITY); + + @BeforeClass + public static void setUp() throws Exception { + System.setProperty("sqlite4java.library.path", "target/test-lib"); + DynamoDBCryptoIntegrationTestBase.setUp(); + dynamoMapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(tableName) + .withKeySchema( + new KeySchemaElement().withAttributeName(hashKeyName).withKeyType(KeyType.HASH)) + .withKeySchema( + new KeySchemaElement().withAttributeName(rangeKeyName).withKeyType(KeyType.RANGE)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(hashKeyName) + .withAttributeType(ScalarAttributeType.S)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(rangeKeyName) + .withAttributeType(ScalarAttributeType.N)); + createTableRequest.setProvisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT); + + if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { + TableUtils.waitUntilActive(dynamo, tableName); } + } - @DynamoDBTable(tableName = tableName) - static public class TestItem { - - private String hashKey; - private Long rangeKey; - private String nonKeyAttribute; - private Set stringSetAttribute; + @DynamoDBTable(tableName = tableName) + public static class TestItem { - @DynamoDBHashKey(attributeName = hashKeyName) - public String getHashKey() { - return hashKey; - } + private String hashKey; + private Long rangeKey; + private String nonKeyAttribute; + private Set stringSetAttribute; - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - @DynamoDBRangeKey(attributeName = rangeKeyName) - public Long getRangeKey() { - return rangeKey; - } + @DynamoDBHashKey(attributeName = hashKeyName) + public String getHashKey() { + return hashKey; + } - public void setRangeKey(Long rangeKey) { - this.rangeKey = rangeKey; - } + public void setHashKey(String hashKey) { + this.hashKey = hashKey; + } - @DoNotTouch - @DynamoDBAttribute(attributeName = nonKeyAttributeName) - public String getNonKeyAttribute() { - return nonKeyAttribute; - } + @DynamoDBRangeKey(attributeName = rangeKeyName) + public Long getRangeKey() { + return rangeKey; + } - public void setNonKeyAttribute(String nonKeyAttribute) { - this.nonKeyAttribute = nonKeyAttribute; - } + public void setRangeKey(Long rangeKey) { + this.rangeKey = rangeKey; + } - @DoNotTouch - @DynamoDBAttribute(attributeName = stringSetAttributeName) - public Set getStringSetAttribute() { - return stringSetAttribute; - } + @DoNotTouch + @DynamoDBAttribute(attributeName = nonKeyAttributeName) + public String getNonKeyAttribute() { + return nonKeyAttribute; + } - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } + public void setNonKeyAttribute(String nonKeyAttribute) { + this.nonKeyAttribute = nonKeyAttribute; + } + @DoNotTouch + @DynamoDBAttribute(attributeName = stringSetAttributeName) + public Set getStringSetAttribute() { + return stringSetAttribute; } - @DynamoDBTable(tableName = tableName) - static public class TestAppendToScalarItem { + public void setStringSetAttribute(Set stringSetAttribute) { + this.stringSetAttribute = stringSetAttribute; + } + } - private String hashKey; - private Long rangeKey; - private Set fakeStringSetAttribute; + @DynamoDBTable(tableName = tableName) + public static class TestAppendToScalarItem { - @DynamoDBHashKey(attributeName = hashKeyName) - public String getHashKey() { - return hashKey; - } + private String hashKey; + private Long rangeKey; + private Set fakeStringSetAttribute; - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } + @DynamoDBHashKey(attributeName = hashKeyName) + public String getHashKey() { + return hashKey; + } - @DynamoDBRangeKey(attributeName = rangeKeyName) - public Long getRangeKey() { - return rangeKey; - } + public void setHashKey(String hashKey) { + this.hashKey = hashKey; + } - public void setRangeKey(Long rangeKey) { - this.rangeKey = rangeKey; - } + @DynamoDBRangeKey(attributeName = rangeKeyName) + public Long getRangeKey() { + return rangeKey; + } - @DynamoDBAttribute(attributeName = nonKeyAttributeName) - public Set getFakeStringSetAttribute() { - return fakeStringSetAttribute; - } + public void setRangeKey(Long rangeKey) { + this.rangeKey = rangeKey; + } - public void setFakeStringSetAttribute(Set stringSetAttribute) { - this.fakeStringSetAttribute = stringSetAttribute; - } + @DynamoDBAttribute(attributeName = nonKeyAttributeName) + public Set getFakeStringSetAttribute() { + return fakeStringSetAttribute; } - /** - * Helper method to create a table in Amazon DynamoDB - */ - protected static void createTestTable( - ProvisionedThroughput provisionedThroughput) { - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(tableName) - .withKeySchema( - new KeySchemaElement().withAttributeName( - hashKeyName).withKeyType( - KeyType.HASH)) - .withKeySchema( - new KeySchemaElement().withAttributeName( - rangeKeyName).withKeyType( - KeyType.RANGE)) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName( - hashKeyName).withAttributeType( - ScalarAttributeType.S)) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName( - rangeKeyName).withAttributeType( - ScalarAttributeType.N)); - createTableRequest.setProvisionedThroughput(provisionedThroughput); - - TableDescription createdTableDescription = dynamo.createTable( - createTableRequest).getTableDescription(); - System.out.println("Created Table: " + createdTableDescription); - assertEquals(tableName, createdTableDescription.getTableName()); - assertNotNull(createdTableDescription.getTableStatus()); - assertEquals(hashKeyName, createdTableDescription - .getKeySchema().get(0).getAttributeName()); - assertEquals(KeyType.HASH.toString(), createdTableDescription - .getKeySchema().get(0).getKeyType()); - assertEquals(rangeKeyName, createdTableDescription - .getKeySchema().get(1).getAttributeName()); - assertEquals(KeyType.RANGE.toString(), createdTableDescription - .getKeySchema().get(1).getKeyType()); + public void setFakeStringSetAttribute(Set stringSetAttribute) { + this.fakeStringSetAttribute = stringSetAttribute; } + } + + /** Helper method to create a table in Amazon DynamoDB */ + protected static void createTestTable(ProvisionedThroughput provisionedThroughput) { + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(tableName) + .withKeySchema( + new KeySchemaElement().withAttributeName(hashKeyName).withKeyType(KeyType.HASH)) + .withKeySchema( + new KeySchemaElement().withAttributeName(rangeKeyName).withKeyType(KeyType.RANGE)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(hashKeyName) + .withAttributeType(ScalarAttributeType.S)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(rangeKeyName) + .withAttributeType(ScalarAttributeType.N)); + createTableRequest.setProvisionedThroughput(provisionedThroughput); + + TableDescription createdTableDescription = + dynamo.createTable(createTableRequest).getTableDescription(); + System.out.println("Created Table: " + createdTableDescription); + assertEquals(tableName, createdTableDescription.getTableName()); + assertNotNull(createdTableDescription.getTableStatus()); + assertEquals(hashKeyName, createdTableDescription.getKeySchema().get(0).getAttributeName()); + assertEquals( + KeyType.HASH.toString(), createdTableDescription.getKeySchema().get(0).getKeyType()); + assertEquals(rangeKeyName, createdTableDescription.getKeySchema().get(1).getAttributeName()); + assertEquals( + KeyType.RANGE.toString(), createdTableDescription.getKeySchema().get(1).getKeyType()); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigITCase.java index 04a1f58c..0b60a4b5 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MapperSaveConfigITCase.java @@ -1,17 +1,23 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + import com.amazonaws.AmazonServiceException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; @@ -20,544 +26,498 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.AfterClass; -import org.testng.annotations.Test; - import java.security.GeneralSecurityException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - +import org.testng.annotations.AfterClass; +import org.testng.annotations.Test; /** - * Tests the behavior of save method of DynamoDBMapper under different - * SaveBehavior configurations. + * Tests the behavior of save method of DynamoDBMapper under different SaveBehavior configurations. */ public class MapperSaveConfigITCase extends MapperSaveConfigCryptoIntegrationTestBase { - @AfterClass - public static void teatDown() throws Exception { - try { -// dynamo.deleteTable(new DeleteTableRequest(tableName)); - } catch (Exception e) { - } + @AfterClass + public static void teatDown() throws Exception { + try { + // dynamo.deleteTable(new DeleteTableRequest(tableName)); + } catch (Exception e) { } - - /********************************************* - ** UPDATE (default) ** - *********************************************/ - - /** - * Tests that a key-only object could be saved with - * UPDATE configuration, even when the key has already existed in the table. + } + + /********************************************* + ** UPDATE (default) ** + *********************************************/ + + /** + * Tests that a key-only object could be saved with UPDATE configuration, even when the key has + * already existed in the table. + */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testDefaultWithOnlyKeyAttributesSpecifiedRecordInTable() throws Exception { + + /* First put a new item (with non-key attribute)*/ + TestItem testItem = putRandomUniqueItem("foo", null); + + /* Put an key-only object with the same key */ + testItem.setNonKeyAttribute(null); + + dynamoMapper.save(testItem, defaultConfig); + + /* The non-key attribute should be nulled out. */ + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + } + + /** + * Tests an edge case that we have fixed according a forum bug report. If the object is only + * specified with key attributes, and such key is not present in the table, we should add this + * object by a key-only put request even if it is using UPDATE configuration. + */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testDefaultWithOnlyKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + + dynamoMapper.save(testItem, defaultConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + } + + /** Update an existing item in the table. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testDefaultWithKeyAndNonKeyAttributesSpecifiedRecordInTable() throws Exception { + + /* First put a new item (without non-key attribute)*/ + TestItem testItem = putRandomUniqueItem(null, null); + String hashKeyValue = testItem.getHashKey(); + Long rangeKeyValue = testItem.getRangeKey(); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(hashKeyValue, returnedObject.getHashKey()); + assertEquals(rangeKeyValue, returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + + /* Put an updated object with the same key and an additional non-key attribute. */ + testItem.setHashKey(hashKeyValue); + testItem.setRangeKey(rangeKeyValue); + testItem.setNonKeyAttribute("update"); + + dynamoMapper.save(testItem, defaultConfig); + returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + } + + /** Use UPDATE to put a new item in the table. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testDefaultWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + testItem.setNonKeyAttribute("new item"); + + dynamoMapper.save(testItem, defaultConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + } + + /********************************************* + ** UPDATE_SKIP_NULL_ATTRIBUTES ** + *********************************************/ + + /** + * When using UPDATE_SKIP_NULL_ATTRIBUTES, key-only update on existing item should not affect the + * item at all, since all the null-valued non-key attributes are ignored. + */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testUpdateSkipNullWithOnlyKeyAttributesSpecifiedRecordInTable() throws Exception { + + /* First put a new item (with non-key attribute)*/ + TestItem testItem = putRandomUniqueItem("foo", null); + + /* Put an key-only object with the same key */ + testItem.setNonKeyAttribute(null); + + dynamoMapper.save(testItem, updateSkipNullConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + /* The non-key attribute should not be removed */ + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals("foo", returnedObject.getNonKeyAttribute()); + } + + /** The behavior should be the same as UPDATE. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testUpdateSkipNullWithOnlyKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + + dynamoMapper.save(testItem, updateSkipNullConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + } + + /** Use UPDATE_SKIP_NULL_ATTRIBUTES to update an existing item in the table. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testUpdateSkipNullWithKeyAndNonKeyAttributesSpecifiedRecordInTable() + throws Exception { + + /* First put a new item (without non-key attribute)*/ + TestItem testItem = putRandomUniqueItem(null, null); + String hashKeyValue = testItem.getHashKey(); + Long rangeKeyValue = testItem.getRangeKey(); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(hashKeyValue, returnedObject.getHashKey()); + assertEquals(rangeKeyValue, returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + + /* Put an updated object with the same key and an additional non-key attribute. */ + String nonKeyAttributeValue = "update"; + testItem.setHashKey(hashKeyValue); + testItem.setRangeKey(rangeKeyValue); + testItem.setNonKeyAttribute(nonKeyAttributeValue); + + dynamoMapper.save(testItem, updateSkipNullConfig); + returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + + /* At last, save the object again, but with non-key attribute set as null. + * This should not change the existing item. */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testDefaultWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (with non-key attribute)*/ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Put an key-only object with the same key */ - testItem.setNonKeyAttribute(null); - - dynamoMapper.save(testItem, defaultConfig); - - /* The non-key attribute should be nulled out. */ - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); + testItem.setNonKeyAttribute(null); + dynamoMapper.save(testItem, updateSkipNullConfig); + returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(nonKeyAttributeValue, returnedObject.getNonKeyAttribute()); + } + + /** Use UPDATE_SKIP_NULL_ATTRIBUTES to put a new item in the table. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testUpdateSkipNullWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() + throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + testItem.setNonKeyAttribute("new item"); + + dynamoMapper.save(testItem, updateSkipNullConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + } + + /********************************************* + ** APPEND_SET ** + *********************************************/ + + /** The behavior should be the same as UPDATE_SKIP_NULL_ATTRIBUTES. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testAppendSetWithOnlyKeyAttributesSpecifiedRecordInTable() throws Exception { + + /* First put a new item (with non-key attributes)*/ + Set randomSet = generateRandomStringSet(3); + TestItem testItem = putRandomUniqueItem("foo", randomSet); + + /* Put an key-only object with the same key */ + testItem.setNonKeyAttribute(null); + testItem.setStringSetAttribute(null); + + dynamoMapper.save(testItem, appendSetConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + /* The non-key attribute should not be removed */ + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals("foo", returnedObject.getNonKeyAttribute()); + assertTrue(assertSetEquals(randomSet, returnedObject.getStringSetAttribute())); + } + + /** The behavior should be the same as UPDATE and UPDATE_SKIP_NULL_ATTRIBUTES. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testAppendSetWithOnlyKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + + dynamoMapper.save(testItem, appendSetConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + assertNull(returnedObject.getStringSetAttribute()); + } + + /** Use APPEND_SET to update an existing item in the table. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testAppendSetWithKeyAndNonKeyAttributesSpecifiedRecordInTable() throws Exception { + + /* First put a new item (without non-key attribute)*/ + TestItem testItem = putRandomUniqueItem(null, null); + String hashKeyValue = testItem.getHashKey(); + Long rangeKeyValue = testItem.getRangeKey(); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(hashKeyValue, returnedObject.getHashKey()); + assertEquals(rangeKeyValue, returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + assertNull(returnedObject.getStringSetAttribute()); + + /* Put an updated object with the same key and an additional non-key attribute. */ + String nonKeyAttributeValue = "update"; + Set stringSetAttributeValue = generateRandomStringSet(3); + testItem.setHashKey(hashKeyValue); + testItem.setRangeKey(rangeKeyValue); + testItem.setNonKeyAttribute(nonKeyAttributeValue); + testItem.setStringSetAttribute(stringSetAttributeValue); + + dynamoMapper.save(testItem, appendSetConfig); + returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + assertTrue( + assertSetEquals(testItem.getStringSetAttribute(), returnedObject.getStringSetAttribute())); + + /* Override nonKeyAttribute and append stringSetAttribute */ + testItem.setNonKeyAttribute("blabla"); + Set appendSetAttribute = generateRandomStringSet(3); + testItem.setStringSetAttribute(appendSetAttribute); + dynamoMapper.save(testItem, appendSetConfig); + returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals("blabla", returnedObject.getNonKeyAttribute()); + // expected set after the append + stringSetAttributeValue.addAll(appendSetAttribute); + assertTrue(assertSetEquals(stringSetAttributeValue, returnedObject.getStringSetAttribute())); + + /* Append on an existing scalar attribute would result in an exception */ + TestAppendToScalarItem testAppendToScalarItem = new TestAppendToScalarItem(); + testAppendToScalarItem.setHashKey(testItem.getHashKey()); + testAppendToScalarItem.setRangeKey(testItem.getRangeKey()); + // this fake set attribute actually points to a scalar attribute + testAppendToScalarItem.setFakeStringSetAttribute(generateRandomStringSet(1)); + try { + dynamoMapper.save(testAppendToScalarItem, appendSetConfig); + fail("Should have thrown a 'Type mismatch' service exception."); + } catch (AmazonServiceException ase) { + assertEquals("ValidationException", ase.getErrorCode()); } - - /** - * Tests an edge case that we have fixed according a forum bug report. If - * the object is only specified with key attributes, and such key is not - * present in the table, we should add this object by a key-only put - * request even if it is using UPDATE configuration. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testDefaultWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, defaultConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Update an existing item in the table. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testDefaultWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (without non-key attribute)*/ - TestItem testItem = putRandomUniqueItem(null, null); - String hashKeyValue = testItem.getHashKey(); - Long rangeKeyValue = testItem.getRangeKey(); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(hashKeyValue, returnedObject.getHashKey()); - assertEquals(rangeKeyValue, returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - - /* Put an updated object with the same key and an additional non-key attribute. */ - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute("update"); - - dynamoMapper.save(testItem, defaultConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /** - * Use UPDATE to put a new item in the table. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testDefaultWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - - dynamoMapper.save(testItem, defaultConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + } + + /** Use APPEND_SET to put a new item in the table. */ + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testAppendSetWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + testItem.setNonKeyAttribute("new item"); + testItem.setStringSetAttribute(generateRandomStringSet(3)); + + dynamoMapper.save(testItem, appendSetConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + assertEquals(testItem.getStringSetAttribute(), returnedObject.getStringSetAttribute()); + } + + /********************************************* + ** CLOBBER ** + *********************************************/ + + /** Use CLOBBER to override the existing item by saving a key-only object. */ + @Test + public void testClobberWithOnlyKeyAttributesSpecifiedRecordInTable() throws Exception { + /* Put the item with non-key attribute */ + TestItem testItem = putRandomUniqueItem("foo", null); + + /* Override the item by saving a key-only object. */ + testItem.setNonKeyAttribute(null); + dynamoMapper.save(testItem, clobberConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + } + + /** Use CLOBBER to put a new item with only key attributes. */ + @Test + public void testClobberWithOnlyKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + + dynamoMapper.save(testItem, clobberConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertNull(returnedObject.getNonKeyAttribute()); + } + + /** Use CLOBBER to override the existing item. */ + @Test + public void testClobberWithKeyAndNonKeyAttributesSpecifiedRecordInTable() throws Exception { + /* Put the item with non-key attribute */ + TestItem testItem = putRandomUniqueItem("foo", null); + + /* Override the item. */ + testItem.setNonKeyAttribute("not foo"); + dynamoMapper.save(testItem, clobberConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + } + + /** Use CLOBBER to put a new item. */ + @Test + public void testClobberWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() throws Exception { + TestItem testItem = new TestItem(); + testItem.setHashKey(UUID.randomUUID().toString()); + testItem.setRangeKey(System.currentTimeMillis()); + testItem.setNonKeyAttribute("new item"); + + dynamoMapper.save(testItem, clobberConfig); + + TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); + + assertNotNull(returnedObject); + assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); + assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); + assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); + } + + private static TestItem putRandomUniqueItem( + String nonKeyAttributeValue, Set stringSetAttributeValue) + throws GeneralSecurityException { + String hashKeyValue = UUID.randomUUID().toString(); + Long rangeKeyValue = System.currentTimeMillis(); + Map item = new HashMap(); + item.put(hashKeyName, new AttributeValue().withS(hashKeyValue)); + item.put(rangeKeyName, new AttributeValue().withN(rangeKeyValue.toString())); + if (null != nonKeyAttributeValue) { + item.put(nonKeyAttributeName, new AttributeValue().withS(nonKeyAttributeValue)); } - - /********************************************* - ** UPDATE_SKIP_NULL_ATTRIBUTES ** - *********************************************/ - - /** - * When using UPDATE_SKIP_NULL_ATTRIBUTES, key-only update on existing item - * should not affect the item at all, since all the null-valued non-key - * attributes are ignored. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testUpdateSkipNullWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (with non-key attribute)*/ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Put an key-only object with the same key */ - testItem.setNonKeyAttribute(null); - - dynamoMapper.save(testItem, updateSkipNullConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - /* The non-key attribute should not be removed */ - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals("foo", returnedObject.getNonKeyAttribute()); - } - - /** - * The behavior should be the same as UPDATE. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testUpdateSkipNullWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, updateSkipNullConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); + if (null != stringSetAttributeValue) { + item.put(stringSetAttributeName, new AttributeValue().withSS(stringSetAttributeValue)); } - /** - * Use UPDATE_SKIP_NULL_ATTRIBUTES to update an existing item in the table. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testUpdateSkipNullWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (without non-key attribute)*/ - TestItem testItem = putRandomUniqueItem(null, null); - String hashKeyValue = testItem.getHashKey(); - Long rangeKeyValue = testItem.getRangeKey(); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(hashKeyValue, returnedObject.getHashKey()); - assertEquals(rangeKeyValue, returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - - /* Put an updated object with the same key and an additional non-key attribute. */ - String nonKeyAttributeValue = "update"; - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute(nonKeyAttributeValue); - - dynamoMapper.save(testItem, updateSkipNullConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - - /* At last, save the object again, but with non-key attribute set as null. - * This should not change the existing item. - */ - testItem.setNonKeyAttribute(null); - dynamoMapper.save(testItem, updateSkipNullConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(nonKeyAttributeValue, returnedObject.getNonKeyAttribute()); - } - - /** - * Use UPDATE_SKIP_NULL_ATTRIBUTES to put a new item in the table. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testUpdateSkipNullWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - - dynamoMapper.save(testItem, updateSkipNullConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /********************************************* - ** APPEND_SET ** - *********************************************/ - - /** - * The behavior should be the same as UPDATE_SKIP_NULL_ATTRIBUTES. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testAppendSetWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (with non-key attributes)*/ - Set randomSet = generateRandomStringSet(3); - TestItem testItem = putRandomUniqueItem("foo", randomSet); - - /* Put an key-only object with the same key */ - testItem.setNonKeyAttribute(null); - testItem.setStringSetAttribute(null); - - dynamoMapper.save(testItem, appendSetConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - /* The non-key attribute should not be removed */ - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals("foo", returnedObject.getNonKeyAttribute()); - assertTrue(assertSetEquals(randomSet, returnedObject.getStringSetAttribute())); - } - - /** - * The behavior should be the same as UPDATE and UPDATE_SKIP_NULL_ATTRIBUTES. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testAppendSetWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, appendSetConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - assertNull(returnedObject.getStringSetAttribute()); - } - - /** - * Use APPEND_SET to update an existing item in the table. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testAppendSetWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (without non-key attribute)*/ - TestItem testItem = putRandomUniqueItem(null, null); - String hashKeyValue = testItem.getHashKey(); - Long rangeKeyValue = testItem.getRangeKey(); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(hashKeyValue, returnedObject.getHashKey()); - assertEquals(rangeKeyValue, returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - assertNull(returnedObject.getStringSetAttribute()); - - /* Put an updated object with the same key and an additional non-key attribute. */ - String nonKeyAttributeValue = "update"; - Set stringSetAttributeValue = generateRandomStringSet(3); - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute(nonKeyAttributeValue); - testItem.setStringSetAttribute(stringSetAttributeValue); - - dynamoMapper.save(testItem, appendSetConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - assertTrue(assertSetEquals(testItem.getStringSetAttribute(), returnedObject.getStringSetAttribute())); - - /* Override nonKeyAttribute and append stringSetAttribute */ - testItem.setNonKeyAttribute("blabla"); - Set appendSetAttribute = generateRandomStringSet(3); - testItem.setStringSetAttribute(appendSetAttribute); - dynamoMapper.save(testItem, appendSetConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals("blabla", returnedObject.getNonKeyAttribute()); - // expected set after the append - stringSetAttributeValue.addAll(appendSetAttribute); - assertTrue(assertSetEquals(stringSetAttributeValue, returnedObject.getStringSetAttribute())); - - /* Append on an existing scalar attribute would result in an exception */ - TestAppendToScalarItem testAppendToScalarItem = new TestAppendToScalarItem(); - testAppendToScalarItem.setHashKey(testItem.getHashKey()); - testAppendToScalarItem.setRangeKey(testItem.getRangeKey()); - // this fake set attribute actually points to a scalar attribute - testAppendToScalarItem.setFakeStringSetAttribute(generateRandomStringSet(1)); - try { - dynamoMapper.save(testAppendToScalarItem, appendSetConfig); - fail("Should have thrown a 'Type mismatch' service exception."); - } catch (AmazonServiceException ase) { - assertEquals("ValidationException", ase.getErrorCode()); - } - } - - /** - * Use APPEND_SET to put a new item in the table. - */ - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testAppendSetWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - testItem.setStringSetAttribute(generateRandomStringSet(3)); - - dynamoMapper.save(testItem, appendSetConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - assertEquals(testItem.getStringSetAttribute(), returnedObject.getStringSetAttribute()); - - } - - /********************************************* - ** CLOBBER ** - *********************************************/ - - /** - * Use CLOBBER to override the existing item by saving a key-only object. - */ - @Test - public void testClobberWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - /* Put the item with non-key attribute */ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Override the item by saving a key-only object. */ - testItem.setNonKeyAttribute(null); - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Use CLOBBER to put a new item with only key attributes. - */ - @Test - public void testClobberWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Use CLOBBER to override the existing item. - */ - @Test - public void testClobberWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - /* Put the item with non-key attribute */ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Override the item. */ - testItem.setNonKeyAttribute("not foo"); - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /** - * Use CLOBBER to put a new item. - */ - @Test - public void testClobberWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - private static TestItem putRandomUniqueItem(String nonKeyAttributeValue, Set stringSetAttributeValue) throws GeneralSecurityException { - String hashKeyValue = UUID.randomUUID().toString(); - Long rangeKeyValue = System.currentTimeMillis(); - Map item = new HashMap(); - item.put(hashKeyName, new AttributeValue().withS(hashKeyValue)); - item.put(rangeKeyName, new AttributeValue().withN(rangeKeyValue.toString())); - if (null != nonKeyAttributeValue) { - item.put(nonKeyAttributeName, new AttributeValue().withS(nonKeyAttributeValue)); - } - if (null != stringSetAttributeValue) { - item.put(stringSetAttributeName, new AttributeValue().withSS(stringSetAttributeValue)); - } - - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder() .withHashKeyName(hashKeyName) .withRangeKeyName(rangeKeyName) .withTableName(tableName) .build(); - Map> flags = encryptor.allEncryptionFlagsExcept(item, hashKeyName, rangeKeyName); - // completely exclude the nonKeyAttributeName; otherwise some of the - // updateSkipNullConfig test will never work - flags.remove(nonKeyAttributeName); - flags.remove(stringSetAttributeName); - item = encryptor.encryptRecord(item, flags, context); -// item = encryptor.encryptAllFieldsExcept(item, context, hashKeyName, rangeKeyName); - dynamo.putItem(new PutItemRequest().withTableName(tableName).withItem(item)); - /* Returns the item as a modeled object. */ - TestItem testItem = new TestItem(); - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute(nonKeyAttributeValue); - testItem.setStringSetAttribute(stringSetAttributeValue); - return testItem; + Map> flags = + encryptor.allEncryptionFlagsExcept(item, hashKeyName, rangeKeyName); + // completely exclude the nonKeyAttributeName; otherwise some of the + // updateSkipNullConfig test will never work + flags.remove(nonKeyAttributeName); + flags.remove(stringSetAttributeName); + item = encryptor.encryptRecord(item, flags, context); + // item = encryptor.encryptAllFieldsExcept(item, context, hashKeyName, rangeKeyName); + dynamo.putItem(new PutItemRequest().withTableName(tableName).withItem(item)); + /* Returns the item as a modeled object. */ + TestItem testItem = new TestItem(); + testItem.setHashKey(hashKeyValue); + testItem.setRangeKey(rangeKeyValue); + testItem.setNonKeyAttribute(nonKeyAttributeValue); + testItem.setStringSetAttribute(stringSetAttributeValue); + return testItem; + } + + private static Set generateRandomStringSet(int size) { + Set result = new HashSet(); + for (int i = 0; i < size; i++) { + result.add(UUID.randomUUID().toString()); } + return result; + } - private static Set generateRandomStringSet(int size) { - Set result = new HashSet(); - for (int i = 0; i < size; i++) { - result.add(UUID.randomUUID().toString()); - } - return result; + private static boolean assertSetEquals(Set expected, Set actual) { + if (expected == null || actual == null) { + return (expected == null && actual == null); + } + if (expected.size() != actual.size()) { + return false; } - - private static boolean assertSetEquals(Set expected, Set actual) { - if (expected == null || actual == null) { - return (expected == null && actual == null); - } - if (expected.size() != actual.size()) { - return false; - } - for (Object item : expected) { - if ( !actual.contains(item) ) { - return false; - } - } - return true; + for (Object item : expected) { + if (!actual.contains(item)) { + return false; + } } -} + return true; + } +} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/NumericSetAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/NumericSetAttributesITCase.java index 1bea350d..33330837 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/NumericSetAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/NumericSetAttributesITCase.java @@ -1,17 +1,19 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -20,9 +22,6 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -35,142 +34,167 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +/** Tests string set attributes */ +public class NumericSetAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { + private static final String INTEGER_ATTRIBUTE = "integerAttribute"; + private static final String FLOAT_OBJECT_ATTRIBUTE = "floatObjectAttribute"; + private static final String DOUBLE_OBJECT_ATTRIBUTE = "doubleObjectAttribute"; + private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; + private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; + private static final String LONG_OBJECT_ATTRIBUTE = "longObjectAttribute"; + private static final String BYTE_OBJECT_ATTRIBUTE = "byteObjectAttribute"; + private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; -/** - * Tests string set attributes - */ -public class NumericSetAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; + private static int byteStart = 1; - private static final String INTEGER_ATTRIBUTE = "integerAttribute"; - private static final String FLOAT_OBJECT_ATTRIBUTE = "floatObjectAttribute"; - private static final String DOUBLE_OBJECT_ATTRIBUTE = "doubleObjectAttribute"; - private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; - private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; - private static final String LONG_OBJECT_ATTRIBUTE = "longObjectAttribute"; - private static final String BYTE_OBJECT_ATTRIBUTE = "byteObjectAttribute"; - private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = 1; - - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for ( int i = 0; i < 5; i++ ) { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withS("" + start++)); - attr.put(INTEGER_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(FLOAT_OBJECT_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(DOUBLE_OBJECT_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(BIG_INTEGER_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(BIG_DECIMAL_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(LONG_OBJECT_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(BYTE_OBJECT_ATTRIBUTE, new AttributeValue().withNS("" + byteStart++, "" + byteStart++, "" + byteStart++)); - attr.put(BOOLEAN_ATTRIBUTE, new AttributeValue().withNS("0", "1")); - attrs.add(attr); - } - }; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperCryptoIntegrationTestBase.setUp(); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() - .withHashKeyName(KEY_NAME) - .withTableName(TABLE_NAME) - .build(); - // Insert the data - for ( Map attr : attrs ) { - attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); - } + private static final List> attrs = + new LinkedList>(); + + // Test data + static { + for (int i = 0; i < 5; i++) { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withS("" + start++)); + attr.put( + INTEGER_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put( + FLOAT_OBJECT_ATTRIBUTE, + new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put( + DOUBLE_OBJECT_ATTRIBUTE, + new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put( + BIG_INTEGER_ATTRIBUTE, + new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put( + BIG_DECIMAL_ATTRIBUTE, + new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put( + LONG_OBJECT_ATTRIBUTE, + new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put( + BYTE_OBJECT_ATTRIBUTE, + new AttributeValue().withNS("" + byteStart++, "" + byteStart++, "" + byteStart++)); + attr.put(BOOLEAN_ATTRIBUTE, new AttributeValue().withNS("0", "1")); + attrs.add(attr); } + } + ; - @Test - public void testLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - for ( Map attr : attrs ) { - NumberSetAttributeTestClass x = util.load(NumberSetAttributeTestClass.class, attr.get(KEY_NAME).getS()); - assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); - - // Convert all numbers to the most inclusive type for easy comparison - assertNumericSetsEquals(x.getBigDecimalAttribute(), attr.get(BIG_DECIMAL_ATTRIBUTE).getNS()); - assertNumericSetsEquals(x.getBigIntegerAttribute(), attr.get(BIG_INTEGER_ATTRIBUTE).getNS()); - assertNumericSetsEquals(x.getFloatObjectAttribute(), attr.get(FLOAT_OBJECT_ATTRIBUTE).getNS()); - assertNumericSetsEquals(x.getDoubleObjectAttribute(), attr.get(DOUBLE_OBJECT_ATTRIBUTE).getNS()); - assertNumericSetsEquals(x.getIntegerAttribute(), attr.get(INTEGER_ATTRIBUTE).getNS()); - assertNumericSetsEquals(x.getLongObjectAttribute(), attr.get(LONG_OBJECT_ATTRIBUTE).getNS()); - assertNumericSetsEquals(x.getByteObjectAttribute(), attr.get(BYTE_OBJECT_ATTRIBUTE).getNS()); - assertSetsEqual(toSet("0", "1"), attr.get(BOOLEAN_ATTRIBUTE).getNS()); - } - } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - NumberSetAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (NumberSetAttributeTestClass obj : objs) { - util.save(obj); - } - - for (NumberSetAttributeTestClass obj : objs) { - NumberSetAttributeTestClass loaded = util.load(NumberSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + // Insert the data + for (Map attr : attrs) { + attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - NumberSetAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (NumberSetAttributeTestClass obj : objs) { - util.save(obj); - } - - for ( NumberSetAttributeTestClass obj : objs ) { - NumberSetAttributeTestClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - assertEquals(replacement, util.load(NumberSetAttributeTestClass.class, obj.getKey())); - } + } + + @Test + public void testLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map attr : attrs) { + NumberSetAttributeTestClass x = + util.load(NumberSetAttributeTestClass.class, attr.get(KEY_NAME).getS()); + assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); + + // Convert all numbers to the most inclusive type for easy comparison + assertNumericSetsEquals(x.getBigDecimalAttribute(), attr.get(BIG_DECIMAL_ATTRIBUTE).getNS()); + assertNumericSetsEquals(x.getBigIntegerAttribute(), attr.get(BIG_INTEGER_ATTRIBUTE).getNS()); + assertNumericSetsEquals( + x.getFloatObjectAttribute(), attr.get(FLOAT_OBJECT_ATTRIBUTE).getNS()); + assertNumericSetsEquals( + x.getDoubleObjectAttribute(), attr.get(DOUBLE_OBJECT_ATTRIBUTE).getNS()); + assertNumericSetsEquals(x.getIntegerAttribute(), attr.get(INTEGER_ATTRIBUTE).getNS()); + assertNumericSetsEquals(x.getLongObjectAttribute(), attr.get(LONG_OBJECT_ATTRIBUTE).getNS()); + assertNumericSetsEquals(x.getByteObjectAttribute(), attr.get(BYTE_OBJECT_ATTRIBUTE).getNS()); + assertSetsEqual(toSet("0", "1"), attr.get(BOOLEAN_ATTRIBUTE).getNS()); + } + } + + @Test + public void testSave() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + NumberSetAttributeTestClass obj = getUniqueObject(); + objs.add(obj); + } + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (NumberSetAttributeTestClass obj : objs) { + util.save(obj); + } + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass loaded = + util.load(NumberSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); + } + } + + @Test + public void testUpdate() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + NumberSetAttributeTestClass obj = getUniqueObject(); + objs.add(obj); + } + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (NumberSetAttributeTestClass obj : objs) { + util.save(obj); + } + + for (NumberSetAttributeTestClass obj : objs) { + NumberSetAttributeTestClass replacement = getUniqueObject(); + replacement.setKey(obj.getKey()); + util.save(replacement); + assertEquals(replacement, util.load(NumberSetAttributeTestClass.class, obj.getKey())); } + } - private NumberSetAttributeTestClass getUniqueObject() { - NumberSetAttributeTestClass obj = new NumberSetAttributeTestClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); - obj.setBigIntegerAttribute(toSet(new BigInteger("" + startKey++), new BigInteger("" + startKey++), new BigInteger("" + startKey++))); - obj.setByteObjectAttribute(toSet(new Byte("" + byteStart++), new Byte("" + byteStart++), new Byte("" + byteStart++))); - obj.setDoubleObjectAttribute(toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); - obj.setFloatObjectAttribute(toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); - obj.setIntegerAttribute(toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); - obj.setLongObjectAttribute(toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); - obj.setBooleanAttribute(toSet(true, false)); - obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); - Set cals = new HashSet(); - for ( Date d : obj.getDateAttribute() ) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(d); - cals.add(cal); - } - obj.setCalendarAttribute(toSet(cals)); - return obj; + private NumberSetAttributeTestClass getUniqueObject() { + NumberSetAttributeTestClass obj = new NumberSetAttributeTestClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setBigDecimalAttribute( + toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); + obj.setBigIntegerAttribute( + toSet( + new BigInteger("" + startKey++), + new BigInteger("" + startKey++), + new BigInteger("" + startKey++))); + obj.setByteObjectAttribute( + toSet(new Byte("" + byteStart++), new Byte("" + byteStart++), new Byte("" + byteStart++))); + obj.setDoubleObjectAttribute( + toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); + obj.setFloatObjectAttribute( + toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); + obj.setIntegerAttribute( + toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); + obj.setLongObjectAttribute( + toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); + obj.setBooleanAttribute(toSet(true, false)); + obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); + Set cals = new HashSet(); + for (Date d : obj.getDateAttribute()) { + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(d); + cals.add(cal); } + obj.setCalendarAttribute(toSet(cals)); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/QueryITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/QueryITCase.java index 4076c8d0..4422beb1 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/QueryITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/QueryITCase.java @@ -1,17 +1,22 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.ConsistentReads; @@ -21,180 +26,165 @@ import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import com.amazonaws.services.dynamodbv2.model.Condition; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -/** - * Integration tests for the query operation on DynamoDBMapper. - */ +/** Integration tests for the query operation on DynamoDBMapper. */ public class QueryITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static final boolean DEBUG = true; - private static final long HASH_KEY = System.currentTimeMillis(); - private static RangeKeyTestClass hashKeyObject; - private static final int TEST_ITEM_NUMBER = 500; - private static DynamoDBMapper mapper; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - - DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig( - ConsistentReads.CONSISTENT); - mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo, mapperConfig); - - putTestData(mapper, TEST_ITEM_NUMBER); - - hashKeyObject = new RangeKeyTestClass(); - hashKeyObject.setKey(HASH_KEY); - } - - @Test - public void testQueryWithPrimaryRangeKey() throws Exception { - DynamoDBQueryExpression queryExpression = new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyObject) - .withRangeKeyCondition( - "rangeKey", - new Condition().withComparisonOperator( - ComparisonOperator.GT).withAttributeValueList( - new AttributeValue().withN("1.0"))) - .withLimit(11); - List list = mapper.query(RangeKeyTestClass.class, - queryExpression); - - int count = 0; - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - count++; - RangeKeyTestClass next = iterator.next(); - assertTrue(next.getRangeKey() > 1.00); - } - - int numMatchingObjects = TEST_ITEM_NUMBER - 2; - if (DEBUG) - System.err.println("count=" + count + ", numMatchingObjects=" + numMatchingObjects); - assertTrue(count == numMatchingObjects); - assertTrue(numMatchingObjects == list.size()); - - assertNotNull(list.get(list.size() / 2)); - assertTrue(list.contains(list.get(list.size() / 2))); - assertTrue(numMatchingObjects == list.toArray().length); - - Thread.sleep(250); - int totalCount = mapper.count(RangeKeyTestClass.class, queryExpression); - assertTrue(numMatchingObjects == totalCount); - - /** - * Tests query with only hash key - */ - queryExpression = new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyObject); - list = mapper.query(RangeKeyTestClass.class, queryExpression); - assertTrue(TEST_ITEM_NUMBER == list.size()); + private static final boolean DEBUG = true; + private static final long HASH_KEY = System.currentTimeMillis(); + private static RangeKeyTestClass hashKeyObject; + private static final int TEST_ITEM_NUMBER = 500; + private static DynamoDBMapper mapper; + + @BeforeClass + public static void setUp() throws Exception { + setUpTableWithRangeAttribute(); + + DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig(ConsistentReads.CONSISTENT); + mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo, mapperConfig); + + putTestData(mapper, TEST_ITEM_NUMBER); + + hashKeyObject = new RangeKeyTestClass(); + hashKeyObject.setKey(HASH_KEY); + } + + @Test + public void testQueryWithPrimaryRangeKey() throws Exception { + DynamoDBQueryExpression queryExpression = + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyObject) + .withRangeKeyCondition( + "rangeKey", + new Condition() + .withComparisonOperator(ComparisonOperator.GT) + .withAttributeValueList(new AttributeValue().withN("1.0"))) + .withLimit(11); + List list = mapper.query(RangeKeyTestClass.class, queryExpression); + + int count = 0; + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + count++; + RangeKeyTestClass next = iterator.next(); + assertTrue(next.getRangeKey() > 1.00); } - /** - * Tests making queries using query filter on non-key attributes. + int numMatchingObjects = TEST_ITEM_NUMBER - 2; + if (DEBUG) System.err.println("count=" + count + ", numMatchingObjects=" + numMatchingObjects); + assertTrue(count == numMatchingObjects); + assertTrue(numMatchingObjects == list.size()); + + assertNotNull(list.get(list.size() / 2)); + assertTrue(list.contains(list.get(list.size() / 2))); + assertTrue(numMatchingObjects == list.toArray().length); + + Thread.sleep(250); + int totalCount = mapper.count(RangeKeyTestClass.class, queryExpression); + assertTrue(numMatchingObjects == totalCount); + + /** Tests query with only hash key */ + queryExpression = + new DynamoDBQueryExpression().withHashKeyValues(hashKeyObject); + list = mapper.query(RangeKeyTestClass.class, queryExpression); + assertTrue(TEST_ITEM_NUMBER == list.size()); + } + + /** Tests making queries using query filter on non-key attributes. */ + @Test + public void testQueryFilter() { + // A random filter condition to be applied to the query. + Random random = new Random(); + int randomFilterValue = random.nextInt(TEST_ITEM_NUMBER); + Condition filterCondition = + new Condition() + .withComparisonOperator(ComparisonOperator.LT) + .withAttributeValueList( + new AttributeValue().withN(Integer.toString(randomFilterValue))); + + /* + * (1) Apply the filter on the range key, in form of key condition */ - @Test - public void testQueryFilter() { - // A random filter condition to be applied to the query. - Random random = new Random(); - int randomFilterValue = random.nextInt(TEST_ITEM_NUMBER); - Condition filterCondition = new Condition().withComparisonOperator( - ComparisonOperator.LT) - .withAttributeValueList( - new AttributeValue().withN(Integer.toString(randomFilterValue))); - - /* - * (1) Apply the filter on the range key, in form of key condition - */ - DynamoDBQueryExpression queryWithRangeKeyCondition = - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyObject).withRangeKeyCondition( - "rangeKey", filterCondition); - List rangeKeyConditionResult = mapper.query( - RangeKeyTestClass.class, queryWithRangeKeyCondition); - - /* - * (2) Apply the filter on the bigDecimalAttribute, in form of query - * filter - */ - DynamoDBQueryExpression queryWithQueryFilterCondition = - new DynamoDBQueryExpression() - .withHashKeyValues(hashKeyObject).withQueryFilter( - Collections.singletonMap("bigDecimalAttribute", - filterCondition)); - List queryFilterResult = mapper.query( - RangeKeyTestClass.class, queryWithQueryFilterCondition); - if (DEBUG) { - System.err.println("rangeKeyConditionResult.size()=" - + rangeKeyConditionResult.size() - + ", queryFilterResult.size()=" + queryFilterResult.size()); - } - assertTrue(rangeKeyConditionResult.size() == queryFilterResult.size()); - for (int i = 0; i < rangeKeyConditionResult.size(); i++) { - assertEquals(rangeKeyConditionResult.get(i), - queryFilterResult.get(i)); - } - } - - /** - * Tests that exception should be raised when user provides an index name - * when making query with the primary range key. + DynamoDBQueryExpression queryWithRangeKeyCondition = + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyObject) + .withRangeKeyCondition("rangeKey", filterCondition); + List rangeKeyConditionResult = + mapper.query(RangeKeyTestClass.class, queryWithRangeKeyCondition); + + /* + * (2) Apply the filter on the bigDecimalAttribute, in form of query + * filter */ - @Test - public void testUnnecessaryIndexNameException() { - try { - DynamoDBMapper mapper = TestDynamoDBMapperFactory - .createDynamoDBMapper(dynamo); - long hashKey = System.currentTimeMillis(); - RangeKeyTestClass keyObject = new RangeKeyTestClass(); - keyObject.setKey(hashKey); - DynamoDBQueryExpression queryExpression = new DynamoDBQueryExpression() - .withHashKeyValues(keyObject); - queryExpression - .withRangeKeyCondition( - "rangeKey", - new Condition().withComparisonOperator( - ComparisonOperator.GT.toString()) - .withAttributeValueList( - new AttributeValue().withN("1.0"))) - .withLimit(11).withIndexName("some_index"); - mapper.query(RangeKeyTestClass.class, queryExpression); - fail("User should not provide index name when making query with the primary range key"); - } catch (IllegalArgumentException expected) { - System.out.println(expected.getMessage()); - } catch (Exception e) { - fail("Should trigger AmazonClientException."); - } - + DynamoDBQueryExpression queryWithQueryFilterCondition = + new DynamoDBQueryExpression() + .withHashKeyValues(hashKeyObject) + .withQueryFilter(Collections.singletonMap("bigDecimalAttribute", filterCondition)); + List queryFilterResult = + mapper.query(RangeKeyTestClass.class, queryWithQueryFilterCondition); + if (DEBUG) { + System.err.println( + "rangeKeyConditionResult.size()=" + + rangeKeyConditionResult.size() + + ", queryFilterResult.size()=" + + queryFilterResult.size()); } - - /** - * Use BatchSave to put some test data into the tested table. Each item is - * hash-keyed by the same value, and range-keyed by numbers starting from 0. - */ - private static void putTestData(DynamoDBMapper mapper, int itemNumber) { - List objs = new ArrayList(); - for (int i = 0; i < itemNumber; i++) { - RangeKeyTestClass obj = new RangeKeyTestClass(); - obj.setKey(HASH_KEY); - obj.setRangeKey(i); - obj.setBigDecimalAttribute(new BigDecimal(i)); - objs.add(obj); - } - mapper.batchSave(objs); + assertTrue(rangeKeyConditionResult.size() == queryFilterResult.size()); + for (int i = 0; i < rangeKeyConditionResult.size(); i++) { + assertEquals(rangeKeyConditionResult.get(i), queryFilterResult.get(i)); + } + } + + /** + * Tests that exception should be raised when user provides an index name when making query with + * the primary range key. + */ + @Test + public void testUnnecessaryIndexNameException() { + try { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + long hashKey = System.currentTimeMillis(); + RangeKeyTestClass keyObject = new RangeKeyTestClass(); + keyObject.setKey(hashKey); + DynamoDBQueryExpression queryExpression = + new DynamoDBQueryExpression().withHashKeyValues(keyObject); + queryExpression + .withRangeKeyCondition( + "rangeKey", + new Condition() + .withComparisonOperator(ComparisonOperator.GT.toString()) + .withAttributeValueList(new AttributeValue().withN("1.0"))) + .withLimit(11) + .withIndexName("some_index"); + mapper.query(RangeKeyTestClass.class, queryExpression); + fail("User should not provide index name when making query with the primary range key"); + } catch (IllegalArgumentException expected) { + System.out.println(expected.getMessage()); + } catch (Exception e) { + fail("Should trigger AmazonClientException."); + } + } + + /** + * Use BatchSave to put some test data into the tested table. Each item is hash-keyed by the same + * value, and range-keyed by numbers starting from 0. + */ + private static void putTestData(DynamoDBMapper mapper, int itemNumber) { + List objs = new ArrayList(); + for (int i = 0; i < itemNumber; i++) { + RangeKeyTestClass obj = new RangeKeyTestClass(); + obj.setKey(HASH_KEY); + obj.setRangeKey(i); + obj.setBigDecimalAttribute(new BigDecimal(i)); + objs.add(obj); } + mapper.batchSave(objs); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/RangeKeyAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/RangeKeyAttributesITCase.java index 670d8595..7e15f78a 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/RangeKeyAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/RangeKeyAttributesITCase.java @@ -1,17 +1,20 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -20,159 +23,166 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - - -/** - * Tests range and hash key combination - */ +/** Tests range and hash key combination */ public class RangeKeyAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static final String RANGE_KEY = "rangeKey"; - private static final String INTEGER_ATTRIBUTE = "integerSetAttribute"; - private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; - private static final String STRING_SET_ATTRIBUTE = "stringSetAttribute"; - private static final String STRING_ATTRIBUTE = "stringAttribute"; - private static final String VERSION_ATTRIBUTE = "version"; - - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for ( int i = 0; i < 5; i++ ) { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withN("" + startKey++)); - attr.put(RANGE_KEY, new AttributeValue().withN("" + start++)); - attr.put(INTEGER_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); - attr.put(BIG_DECIMAL_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + start++)); - attr.put(STRING_SET_ATTRIBUTE, new AttributeValue().withSS("" + start++, "" + start++, "" + start++)); - attr.put(VERSION_ATTRIBUTE, new AttributeValue().withN("1")); - - attrs.add(attr); - } - }; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() + private static final String RANGE_KEY = "rangeKey"; + private static final String INTEGER_ATTRIBUTE = "integerSetAttribute"; + private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; + private static final String STRING_SET_ATTRIBUTE = "stringSetAttribute"; + private static final String STRING_ATTRIBUTE = "stringAttribute"; + private static final String VERSION_ATTRIBUTE = "version"; + + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; + + private static final List> attrs = + new LinkedList>(); + + // Test data + static { + for (int i = 0; i < 5; i++) { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withN("" + startKey++)); + attr.put(RANGE_KEY, new AttributeValue().withN("" + start++)); + attr.put( + INTEGER_ATTRIBUTE, new AttributeValue().withNS("" + start++, "" + start++, "" + start++)); + attr.put(BIG_DECIMAL_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + start++)); + attr.put( + STRING_SET_ATTRIBUTE, + new AttributeValue().withSS("" + start++, "" + start++, "" + start++)); + attr.put(VERSION_ATTRIBUTE, new AttributeValue().withN("1")); + + attrs.add(attr); + } + } + ; + + @BeforeClass + public static void setUp() throws Exception { + setUpTableWithRangeAttribute(); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder() .withHashKeyName(KEY_NAME) .withRangeKeyName(RANGE_KEY) .withTableName(TABLE_WITH_RANGE_ATTRIBUTE) .build(); - // Insert the data - for ( Map attr : attrs ) { - attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME, - RANGE_KEY, VERSION_ATTRIBUTE, BIG_DECIMAL_ATTRIBUTE); - dynamo.putItem(new PutItemRequest(TABLE_WITH_RANGE_ATTRIBUTE, attr)); - } + // Insert the data + for (Map attr : attrs) { + attr = + encryptor.encryptAllFieldsExcept( + attr, context, KEY_NAME, RANGE_KEY, VERSION_ATTRIBUTE, BIG_DECIMAL_ATTRIBUTE); + dynamo.putItem(new PutItemRequest(TABLE_WITH_RANGE_ATTRIBUTE, attr)); } - - @Test - public void testLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - for ( Map attr : attrs ) { - RangeKeyTestClass x = util.load(newRangeKey(Long.parseLong(attr.get(KEY_NAME).getN()), - Double.parseDouble(attr.get(RANGE_KEY).getN()))); - - // Convert all numbers to the most inclusive type for easy - // comparison - assertEquals(new BigDecimal(x.getKey()), new BigDecimal(attr.get(KEY_NAME).getN())); - assertEquals(new BigDecimal(x.getRangeKey()), new BigDecimal(attr.get(RANGE_KEY).getN())); - assertEquals(new BigDecimal(x.getVersion()), new BigDecimal(attr.get(VERSION_ATTRIBUTE).getN())); - assertEquals(x.getBigDecimalAttribute(), new BigDecimal(attr.get(BIG_DECIMAL_ATTRIBUTE).getN())); - assertNumericSetsEquals(x.getIntegerAttribute(), attr.get(INTEGER_ATTRIBUTE).getNS()); - assertEquals(x.getStringAttribute(), attr.get(STRING_ATTRIBUTE).getS()); - assertSetsEqual(x.getStringSetAttribute(), toSet(attr.get(STRING_SET_ATTRIBUTE).getSS())); - } + } + + @Test + public void testLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map attr : attrs) { + RangeKeyTestClass x = + util.load( + newRangeKey( + Long.parseLong(attr.get(KEY_NAME).getN()), + Double.parseDouble(attr.get(RANGE_KEY).getN()))); + + // Convert all numbers to the most inclusive type for easy + // comparison + assertEquals(new BigDecimal(x.getKey()), new BigDecimal(attr.get(KEY_NAME).getN())); + assertEquals(new BigDecimal(x.getRangeKey()), new BigDecimal(attr.get(RANGE_KEY).getN())); + assertEquals( + new BigDecimal(x.getVersion()), new BigDecimal(attr.get(VERSION_ATTRIBUTE).getN())); + assertEquals( + x.getBigDecimalAttribute(), new BigDecimal(attr.get(BIG_DECIMAL_ATTRIBUTE).getN())); + assertNumericSetsEquals(x.getIntegerAttribute(), attr.get(INTEGER_ATTRIBUTE).getNS()); + assertEquals(x.getStringAttribute(), attr.get(STRING_ATTRIBUTE).getS()); + assertSetsEqual(x.getStringSetAttribute(), toSet(attr.get(STRING_SET_ATTRIBUTE).getSS())); + } + } + + private RangeKeyTestClass newRangeKey(long hashKey, double rangeKey) { + RangeKeyTestClass obj = new RangeKeyTestClass(); + obj.setKey(hashKey); + obj.setRangeKey(rangeKey); + return obj; + } + + @Test + public void testSave() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + RangeKeyTestClass obj = getUniqueObject(); + objs.add(obj); } - - private RangeKeyTestClass newRangeKey(long hashKey, double rangeKey) { - RangeKeyTestClass obj = new RangeKeyTestClass(); - obj.setKey(hashKey); - obj.setRangeKey(rangeKey); - return obj; + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (RangeKeyTestClass obj : objs) { + util.save(obj); } - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - RangeKeyTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (RangeKeyTestClass obj : objs) { - util.save(obj); - } - - for (RangeKeyTestClass obj : objs) { - RangeKeyTestClass loaded = util.load(RangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(obj, loaded); - } + for (RangeKeyTestClass obj : objs) { + RangeKeyTestClass loaded = + util.load(RangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); + assertEquals(obj, loaded); + } + } + + @Test + public void testUpdate() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + RangeKeyTestClass obj = getUniqueObject(); + objs.add(obj); } - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - RangeKeyTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (RangeKeyTestClass obj : objs) { - util.save(obj); - } - - for ( RangeKeyTestClass obj : objs ) { - RangeKeyTestClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - replacement.setRangeKey(obj.getRangeKey()); - replacement.setVersion(obj.getVersion()); - util.save(replacement); - - RangeKeyTestClass loadedObject = util.load(RangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(replacement, loadedObject); - - // If we try to update the old version, we should get an error - replacement.setVersion(replacement.getVersion() - 1); - try { - util.save(replacement); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - } + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (RangeKeyTestClass obj : objs) { + util.save(obj); } - private RangeKeyTestClass getUniqueObject() { - RangeKeyTestClass obj = new RangeKeyTestClass(); - obj.setKey(startKey++); - obj.setIntegerAttribute(toSet(start++, start++, start++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setRangeKey(start++); - obj.setStringAttribute("" + startKey++); - obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); - return obj; + for (RangeKeyTestClass obj : objs) { + RangeKeyTestClass replacement = getUniqueObject(); + replacement.setKey(obj.getKey()); + replacement.setRangeKey(obj.getRangeKey()); + replacement.setVersion(obj.getVersion()); + util.save(replacement); + + RangeKeyTestClass loadedObject = + util.load(RangeKeyTestClass.class, obj.getKey(), obj.getRangeKey()); + assertEquals(replacement, loadedObject); + + // If we try to update the old version, we should get an error + replacement.setVersion(replacement.getVersion() - 1); + try { + util.save(replacement); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } } + } + + private RangeKeyTestClass getUniqueObject() { + RangeKeyTestClass obj = new RangeKeyTestClass(); + obj.setKey(startKey++); + obj.setIntegerAttribute(toSet(start++, start++, start++)); + obj.setBigDecimalAttribute(new BigDecimal(startKey++)); + obj.setRangeKey(start++); + obj.setStringAttribute("" + startKey++); + obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ScanITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ScanITCase.java index 9a2af0ad..26127ced 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ScanITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/ScanITCase.java @@ -12,13 +12,17 @@ */ package com.amazonaws.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + import com.amazonaws.AmazonServiceException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedParallelScanList; -import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList; import com.amazonaws.services.dynamodbv2.datamodeling.ScanResultPage; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; @@ -32,247 +36,252 @@ import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.util.TableUtils; import com.amazonaws.util.ImmutableMapParameter; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -/** - * Integration tests for the scan operation on DynamoDBMapper. - */ +/** Integration tests for the scan operation on DynamoDBMapper. */ public class ScanITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static final String TABLE_NAME = "aws-java-sdk-util-scan-crypto"; - /** - * We set a small limit in order to test the behavior of PaginatedList - * when it could not load all the scan result in one batch. - */ - private static final int SCAN_LIMIT = 10; - private static final int PARALLEL_SCAN_SEGMENTS = 5; - - private static void createTestData() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (int i = 0; i < 500; i++) { - util.save(new SimpleClass(Integer.toString(i), Integer.toString(i))); - } - } - - @BeforeClass - public static void setUpTestData() throws Exception { - String keyName = "id"; - CreateTableRequest createTableRequest = new CreateTableRequest() - .withTableName(TABLE_NAME) - .withKeySchema(new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH)) - .withAttributeDefinitions( - new AttributeDefinition().withAttributeName(keyName).withAttributeType( - ScalarAttributeType.S)); - createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(10L) - .withWriteCapacityUnits(5L)); - - TableUtils.createTableIfNotExists(dynamo, createTableRequest); - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - - createTestData(); - } + private static final String TABLE_NAME = "aws-java-sdk-util-scan-crypto"; + /** + * We set a small limit in order to test the behavior of PaginatedList when it could not load all + * the scan result in one batch. + */ + private static final int SCAN_LIMIT = 10; + private static final int PARALLEL_SCAN_SEGMENTS = 5; - @Test - public void testScan() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withLimit(SCAN_LIMIT); - scanExpression.addFilterCondition("value", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); - scanExpression.addFilterCondition("extraData", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); - List list = util.scan(SimpleClass.class, scanExpression); - - int count = 0; - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - } + private static void createTestData() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (int i = 0; i < 500; i++) { + util.save(new SimpleClass(Integer.toString(i), Integer.toString(i))); + } + } + + @BeforeClass + public static void setUpTestData() throws Exception { + String keyName = "id"; + CreateTableRequest createTableRequest = + new CreateTableRequest() + .withTableName(TABLE_NAME) + .withKeySchema( + new KeySchemaElement().withAttributeName(keyName).withKeyType(KeyType.HASH)) + .withAttributeDefinitions( + new AttributeDefinition() + .withAttributeName(keyName) + .withAttributeType(ScalarAttributeType.S)); + createTableRequest.setProvisionedThroughput( + new ProvisionedThroughput().withReadCapacityUnits(10L).withWriteCapacityUnits(5L)); + + TableUtils.createTableIfNotExists(dynamo, createTableRequest); + TableUtils.waitUntilActive(dynamo, TABLE_NAME); + + createTestData(); + } + + @Test + public void testScan() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withLimit(SCAN_LIMIT); + scanExpression.addFilterCondition( + "value", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); + scanExpression.addFilterCondition( + "extraData", + new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); + List list = util.scan(SimpleClass.class, scanExpression); + + int count = 0; + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + count++; + SimpleClass next = iterator.next(); + assertNotNull(next.getExtraData()); + assertNotNull(next.getValue()); + } - int totalCount = util.count(SimpleClass.class, scanExpression); + int totalCount = util.count(SimpleClass.class, scanExpression); - assertNotNull(list.get(totalCount / 2)); - assertTrue(totalCount == count); - assertTrue(totalCount == list.size()); + assertNotNull(list.get(totalCount / 2)); + assertTrue(totalCount == count); + assertTrue(totalCount == list.size()); - assertTrue(list.contains(list.get(list.size() / 2))); - assertTrue(count == list.toArray().length); - } + assertTrue(list.contains(list.get(list.size() / 2))); + assertTrue(count == list.toArray().length); + } - /** - * Tests scanning the table with AND/OR logic operator. - */ - @Test - public void testScanWithConditionalOperator() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + /** Tests scanning the table with AND/OR logic operator. */ + @Test + public void testScanWithConditionalOperator() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression() + DynamoDBScanExpression scanExpression = + new DynamoDBScanExpression() .withLimit(SCAN_LIMIT) - .withScanFilter(ImmutableMapParameter.of( + .withScanFilter( + ImmutableMapParameter.of( "value", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL), - "non-existent-field", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL) - )) + "non-existent-field", + new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL))) .withConditionalOperator(ConditionalOperator.AND); - List andConditionResult = mapper.scan(SimpleClass.class, scanExpression); - assertTrue(andConditionResult.isEmpty()); - - List orConditionResult = mapper.scan(SimpleClass.class, - scanExpression.withConditionalOperator(ConditionalOperator.OR)); - assertFalse(orConditionResult.isEmpty()); + List andConditionResult = mapper.scan(SimpleClass.class, scanExpression); + assertTrue(andConditionResult.isEmpty()); + + List orConditionResult = + mapper.scan( + SimpleClass.class, scanExpression.withConditionalOperator(ConditionalOperator.OR)); + assertFalse(orConditionResult.isEmpty()); + } + + @Test + public void testParallelScan() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withLimit(SCAN_LIMIT); + scanExpression.addFilterCondition( + "value", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); + scanExpression.addFilterCondition( + "extraData", + new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); + + PaginatedParallelScanList parallelScanList = + util.parallelScan(SimpleClass.class, scanExpression, PARALLEL_SCAN_SEGMENTS); + int count = 0; + Iterator iterator = parallelScanList.iterator(); + HashMap allDataAppearance = new HashMap(); + for (int i = 0; i < 500; i++) { + allDataAppearance.put("" + i, false); } - - @Test - public void testParallelScan() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withLimit(SCAN_LIMIT); - scanExpression.addFilterCondition("value", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); - scanExpression.addFilterCondition("extraData", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); - - PaginatedParallelScanList parallelScanList = util.parallelScan(SimpleClass.class, scanExpression, PARALLEL_SCAN_SEGMENTS); - int count = 0; - Iterator iterator = parallelScanList.iterator(); - HashMap allDataAppearance = new HashMap(); - for (int i=0;i<500;i++) { - allDataAppearance.put("" + i, false); - } - while (iterator.hasNext()) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - allDataAppearance.put(next.getId(), true); - } - assertFalse(allDataAppearance.values().contains(false)); - - int totalCount = util.count(SimpleClass.class, scanExpression); - - assertNotNull(parallelScanList.get(totalCount / 2)); - assertTrue(totalCount == count); - assertTrue(totalCount == parallelScanList.size()); - - assertTrue(parallelScanList.contains(parallelScanList.get(parallelScanList.size() / 2))); - assertTrue(count == parallelScanList.toArray().length); - + while (iterator.hasNext()) { + count++; + SimpleClass next = iterator.next(); + assertNotNull(next.getExtraData()); + assertNotNull(next.getValue()); + allDataAppearance.put(next.getId(), true); } - - @Test - public void testParallelScanExceptionHandling() { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - int INVALID_LIMIT = 0; - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withLimit(INVALID_LIMIT); - try { - // Using 2 segments to reduce the chance of a RejectedExecutionException occurring when too many threads are spun up - // An alternative would be to maintain a higher segment count, but re-test when a RejectedExecutionException occurs - PaginatedParallelScanList parallelScanList = util.parallelScan(SimpleClass.class, scanExpression, 2); - fail("Test succeeded when it should have failed"); - } catch (AmazonServiceException ase) { - assertNotNull(ase.getErrorCode()); - assertNotNull(ase.getErrorType()); - assertNotNull(ase.getMessage()); - } catch (Exception e) { - fail("Should have seen the AmazonServiceException"); - } + assertFalse(allDataAppearance.values().contains(false)); + + int totalCount = util.count(SimpleClass.class, scanExpression); + + assertNotNull(parallelScanList.get(totalCount / 2)); + assertTrue(totalCount == count); + assertTrue(totalCount == parallelScanList.size()); + + assertTrue(parallelScanList.contains(parallelScanList.get(parallelScanList.size() / 2))); + assertTrue(count == parallelScanList.toArray().length); + } + + @Test + public void testParallelScanExceptionHandling() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + int INVALID_LIMIT = 0; + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withLimit(INVALID_LIMIT); + try { + // Using 2 segments to reduce the chance of a RejectedExecutionException occurring when too + // many threads are spun up + // An alternative would be to maintain a higher segment count, but re-test when a + // RejectedExecutionException occurs + PaginatedParallelScanList parallelScanList = + util.parallelScan(SimpleClass.class, scanExpression, 2); + fail("Test succeeded when it should have failed"); + } catch (AmazonServiceException ase) { + assertNotNull(ase.getErrorCode()); + assertNotNull(ase.getErrorType()); + assertNotNull(ase.getMessage()); + } catch (Exception e) { + fail("Should have seen the AmazonServiceException"); } + } + + @Test + public void testScanPage() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); + scanExpression.addFilterCondition( + "value", new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); + scanExpression.addFilterCondition( + "extraData", + new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); + int limit = 3; + scanExpression.setLimit(limit); + ScanResultPage result = util.scanPage(SimpleClass.class, scanExpression); + + int count = 0; + Iterator iterator = result.getResults().iterator(); + Set seen = new HashSet(); + while (iterator.hasNext()) { + count++; + SimpleClass next = iterator.next(); + assertNotNull(next.getExtraData()); + assertNotNull(next.getValue()); + assertTrue(seen.add(next)); + } + + assertTrue(limit == count); + assertTrue(count == result.getResults().toArray().length); - @Test - public void testScanPage() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); - scanExpression.addFilterCondition("value", - new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); - scanExpression.addFilterCondition("extraData", - new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL.toString())); - int limit = 3; - scanExpression.setLimit(limit); - ScanResultPage result = util.scanPage(SimpleClass.class, scanExpression); - - int count = 0; - Iterator iterator = result.getResults().iterator(); - Set seen = new HashSet(); - while ( iterator.hasNext() ) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - assertTrue(seen.add(next)); - } - - assertTrue(limit == count); - assertTrue(count == result.getResults().toArray().length); - - scanExpression.setExclusiveStartKey(result.getLastEvaluatedKey()); - result = util.scanPage(SimpleClass.class, scanExpression); - - iterator = result.getResults().iterator(); - count = 0; - while ( iterator.hasNext() ) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - assertTrue(seen.add(next)); - } - - assertTrue(limit == count); - assertTrue(count == result.getResults().toArray().length); + scanExpression.setExclusiveStartKey(result.getLastEvaluatedKey()); + result = util.scanPage(SimpleClass.class, scanExpression); + iterator = result.getResults().iterator(); + count = 0; + while (iterator.hasNext()) { + count++; + SimpleClass next = iterator.next(); + assertNotNull(next.getExtraData()); + assertNotNull(next.getValue()); + assertTrue(seen.add(next)); } - @DynamoDBTable(tableName = "aws-java-sdk-util-scan-crypto") - public static final class SimpleClass { - private String id; - private String value; - private String extraData; + assertTrue(limit == count); + assertTrue(count == result.getResults().toArray().length); + } - public SimpleClass() {} + @DynamoDBTable(tableName = "aws-java-sdk-util-scan-crypto") + public static final class SimpleClass { + private String id; + private String value; + private String extraData; - public SimpleClass(String id, String value) { - this.id = id; - this.value = value; - this.extraData = UUID.randomUUID().toString(); - } + public SimpleClass() {} - @DynamoDBHashKey - public String getId() { - return id; - } + public SimpleClass(String id, String value) { + this.id = id; + this.value = value; + this.extraData = UUID.randomUUID().toString(); + } - public void setId(String id) { - this.id = id; - } + @DynamoDBHashKey + public String getId() { + return id; + } - public String getValue() { - return value; - } + public void setId(String id) { + this.id = id; + } - public void setValue(String value) { - this.value = value; - } + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } - public String getExtraData() { - return extraData; - } + public String getExtraData() { + return extraData; + } - public void setExtraData(String extraData) { - this.extraData = extraData; - } + public void setExtraData(String extraData) { + this.extraData = extraData; } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleNumericAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleNumericAttributesITCase.java index 84c5b4df..59a9950f 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleNumericAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleNumericAttributesITCase.java @@ -1,17 +1,22 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -22,9 +27,6 @@ import com.amazonaws.services.dynamodbv2.model.GetItemRequest; import com.amazonaws.services.dynamodbv2.model.GetItemResult; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -35,250 +37,265 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; +/** Tests numeric attributes */ +public class SimpleNumericAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { + private static final String INT_ATTRIBUTE = "intAttribute"; + private static final String INTEGER_ATTRIBUTE = "integerAttribute"; + private static final String FLOAT_ATTRIBUTE = "floatAttribute"; + private static final String FLOAT_OBJECT_ATTRIBUTE = "floatObjectAttribute"; + private static final String DOUBLE_ATTRIBUTE = "doubleAttribute"; + private static final String DOUBLE_OBJECT_ATTRIBUTE = "doubleObjectAttribute"; + private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; + private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; + private static final String LONG_ATTRIBUTE = "longAttribute"; + private static final String LONG_OBJECT_ATTRIBUTE = "longObjectAttribute"; + private static final String BYTE_ATTRIBUTE = "byteAttribute"; + private static final String BYTE_OBJECT_ATTRIBUTE = "byteObjectAttribute"; + private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; + private static final String BOOLEAN_OBJECT_ATTRIBUTE = "booleanObjectAttribute"; + private static final String SHORT_ATTRIBUTE = "shortAttribute"; + private static final String SHORT_OBJECT_ATTRIBUTE = "shortObjectAttribute"; -/** - * Tests numeric attributes - */ -public class SimpleNumericAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { + // We don't start with the current system millis like other tests because + // it's out of the range of some data types + private static int start = 1; + private static int byteStart = -127; + + private static final List> attrs = + new LinkedList>(); - private static final String INT_ATTRIBUTE = "intAttribute"; - private static final String INTEGER_ATTRIBUTE = "integerAttribute"; - private static final String FLOAT_ATTRIBUTE = "floatAttribute"; - private static final String FLOAT_OBJECT_ATTRIBUTE = "floatObjectAttribute"; - private static final String DOUBLE_ATTRIBUTE = "doubleAttribute"; - private static final String DOUBLE_OBJECT_ATTRIBUTE = "doubleObjectAttribute"; - private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; - private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; - private static final String LONG_ATTRIBUTE = "longAttribute"; - private static final String LONG_OBJECT_ATTRIBUTE = "longObjectAttribute"; - private static final String BYTE_ATTRIBUTE = "byteAttribute"; - private static final String BYTE_OBJECT_ATTRIBUTE = "byteObjectAttribute"; - private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; - private static final String BOOLEAN_OBJECT_ATTRIBUTE = "booleanObjectAttribute"; - private static final String SHORT_ATTRIBUTE = "shortAttribute"; - private static final String SHORT_OBJECT_ATTRIBUTE = "shortObjectAttribute"; - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = -127; - - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for ( int i = 0; i < 5; i++ ) { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withS("" + start++)); - attr.put(INT_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(INTEGER_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(FLOAT_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(FLOAT_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(DOUBLE_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(DOUBLE_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(BIG_INTEGER_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(BIG_DECIMAL_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(LONG_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(LONG_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + start++)); - attr.put(BYTE_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); - attr.put(BYTE_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); - attr.put(BOOLEAN_ATTRIBUTE, new AttributeValue().withN(start++ % 2 == 0 ? "1" : "0")); - attr.put(BOOLEAN_OBJECT_ATTRIBUTE, new AttributeValue().withN(start++ % 2 == 0 ? "1" : "0")); - attr.put(SHORT_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); - attr.put(SHORT_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); - attrs.add(attr); - } - }; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperCryptoIntegrationTestBase.setUp(); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() - .withHashKeyName(KEY_NAME) - .withTableName(TABLE_NAME) - .build(); - // Insert the data - for ( Map attr : attrs ) { - attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); - } + // Test data + static { + for (int i = 0; i < 5; i++) { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withS("" + start++)); + attr.put(INT_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(INTEGER_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(FLOAT_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(FLOAT_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(DOUBLE_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(DOUBLE_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(BIG_INTEGER_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(BIG_DECIMAL_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(LONG_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(LONG_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + start++)); + attr.put(BYTE_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); + attr.put(BYTE_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); + attr.put(BOOLEAN_ATTRIBUTE, new AttributeValue().withN(start++ % 2 == 0 ? "1" : "0")); + attr.put(BOOLEAN_OBJECT_ATTRIBUTE, new AttributeValue().withN(start++ % 2 == 0 ? "1" : "0")); + attr.put(SHORT_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); + attr.put(SHORT_OBJECT_ATTRIBUTE, new AttributeValue().withN("" + byteStart++)); + attrs.add(attr); } + } + ; - private NumberAttributeTestClass getKeyObject(String key) { - NumberAttributeTestClass obj = new NumberAttributeTestClass(); - obj.setKey(key); - return obj; + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + // Insert the data + for (Map attr : attrs) { + attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); } - - @Test - public void testLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - for ( Map attr : attrs ) { - NumberAttributeTestClass x = util.load(getKeyObject(attr.get(KEY_NAME).getS())); - assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); - - // Convert all numbers to the most inclusive type for easy comparison - assertEquals(x.getBigDecimalAttribute(), new BigDecimal(attr.get(BIG_DECIMAL_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getBigIntegerAttribute()), new BigDecimal(attr.get(BIG_INTEGER_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getFloatAttribute()), new BigDecimal(attr.get(FLOAT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getFloatObjectAttribute()), new BigDecimal(attr.get(FLOAT_OBJECT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getDoubleAttribute()), new BigDecimal(attr.get(DOUBLE_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getDoubleObjectAttribute()), new BigDecimal(attr.get(DOUBLE_OBJECT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getIntAttribute()), new BigDecimal(attr.get(INT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getIntegerAttribute()), new BigDecimal(attr.get(INTEGER_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getLongAttribute()), new BigDecimal(attr.get(LONG_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getLongObjectAttribute()), new BigDecimal(attr.get(LONG_OBJECT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getByteAttribute()), new BigDecimal(attr.get(BYTE_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getByteObjectAttribute()), new BigDecimal(attr.get(BYTE_OBJECT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getShortAttribute()), new BigDecimal(attr.get(SHORT_ATTRIBUTE).getN())); - assertEquals(new BigDecimal(x.getShortObjectAttribute()), new BigDecimal(attr.get(SHORT_OBJECT_ATTRIBUTE).getN())); - assertEquals(x.isBooleanAttribute(), attr.get(BOOLEAN_ATTRIBUTE).getN().equals("1")); - assertEquals((Object) x.getBooleanObjectAttribute(), (Object) attr.get(BOOLEAN_OBJECT_ATTRIBUTE).getN().equals("1")); - } - - // Test loading an object that doesn't exist - assertNull(util.load(getKeyObject("does not exist"))); + } + + private NumberAttributeTestClass getKeyObject(String key) { + NumberAttributeTestClass obj = new NumberAttributeTestClass(); + obj.setKey(key); + return obj; + } + + @Test + public void testLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map attr : attrs) { + NumberAttributeTestClass x = util.load(getKeyObject(attr.get(KEY_NAME).getS())); + assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); + + // Convert all numbers to the most inclusive type for easy comparison + assertEquals( + x.getBigDecimalAttribute(), new BigDecimal(attr.get(BIG_DECIMAL_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getBigIntegerAttribute()), + new BigDecimal(attr.get(BIG_INTEGER_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getFloatAttribute()), new BigDecimal(attr.get(FLOAT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getFloatObjectAttribute()), + new BigDecimal(attr.get(FLOAT_OBJECT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getDoubleAttribute()), + new BigDecimal(attr.get(DOUBLE_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getDoubleObjectAttribute()), + new BigDecimal(attr.get(DOUBLE_OBJECT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getIntAttribute()), new BigDecimal(attr.get(INT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getIntegerAttribute()), + new BigDecimal(attr.get(INTEGER_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getLongAttribute()), new BigDecimal(attr.get(LONG_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getLongObjectAttribute()), + new BigDecimal(attr.get(LONG_OBJECT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getByteAttribute()), new BigDecimal(attr.get(BYTE_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getByteObjectAttribute()), + new BigDecimal(attr.get(BYTE_OBJECT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getShortAttribute()), new BigDecimal(attr.get(SHORT_ATTRIBUTE).getN())); + assertEquals( + new BigDecimal(x.getShortObjectAttribute()), + new BigDecimal(attr.get(SHORT_OBJECT_ATTRIBUTE).getN())); + assertEquals(x.isBooleanAttribute(), attr.get(BOOLEAN_ATTRIBUTE).getN().equals("1")); + assertEquals( + (Object) x.getBooleanObjectAttribute(), + (Object) attr.get(BOOLEAN_OBJECT_ATTRIBUTE).getN().equals("1")); + } + + // Test loading an object that doesn't exist + assertNull(util.load(getKeyObject("does not exist"))); + } + + @Test + public void testSave() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + NumberAttributeTestClass obj = getUniqueObject(); + objs.add(obj); + } + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (NumberAttributeTestClass obj : objs) { + util.save(obj); } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - NumberAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (NumberAttributeTestClass obj : objs) { - util.save(obj); - } - - for (NumberAttributeTestClass obj : objs) { - NumberAttributeTestClass loaded = util.load(obj); - loaded.setIgnored(obj.getIgnored()); - assertEquals(obj, loaded); - } - } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - NumberAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (NumberAttributeTestClass obj : objs) { - util.save(obj); - } - - for ( NumberAttributeTestClass obj : objs ) { - NumberAttributeTestClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - - NumberAttributeTestClass loadedObject = util.load(obj); - - assertFalse(replacement.getIgnored().equals(loadedObject.getIgnored())); - loadedObject.setIgnored(replacement.getIgnored()); - assertEquals(replacement, loadedObject); - } + + for (NumberAttributeTestClass obj : objs) { + NumberAttributeTestClass loaded = util.load(obj); + loaded.setIgnored(obj.getIgnored()); + assertEquals(obj, loaded); } - - /** - * Tests automatically setting a hash key upon saving. - */ - @Test - public void testSetHashKey() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - NumberAttributeTestClass obj = getUniqueObject(); - obj.setKey(null); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (NumberAttributeTestClass obj : objs) { - assertNull(obj.getKey()); - util.save(obj); - assertNotNull(obj.getKey()); - NumberAttributeTestClass loadedObject = util.load(obj); - - assertFalse(obj.getIgnored().equals(loadedObject.getIgnored())); - loadedObject.setIgnored(obj.getIgnored()); - assertEquals(obj, loadedObject); - } + } + + @Test + public void testUpdate() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + NumberAttributeTestClass obj = getUniqueObject(); + objs.add(obj); + } + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (NumberAttributeTestClass obj : objs) { + util.save(obj); } - @Test - public void testDelete() throws Exception { - NumberAttributeTestClass obj = getUniqueObject(); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(obj); + for (NumberAttributeTestClass obj : objs) { + NumberAttributeTestClass replacement = getUniqueObject(); + replacement.setKey(obj.getKey()); + util.save(replacement); - NumberAttributeTestClass loaded = util.load(NumberAttributeTestClass.class, obj.getKey()); - loaded.setIgnored(obj.getIgnored()); - assertEquals(obj, loaded); + NumberAttributeTestClass loadedObject = util.load(obj); - util.delete(obj); - assertNull(util.load(NumberAttributeTestClass.class, obj.getKey())); + assertFalse(replacement.getIgnored().equals(loadedObject.getIgnored())); + loadedObject.setIgnored(replacement.getIgnored()); + assertEquals(replacement, loadedObject); } - - @Test - public void performanceTest() throws Exception { - NumberAttributeTestClass obj = getUniqueObject(); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); - HashMap key = new HashMap(); - key.put(KEY_NAME, new AttributeValue().withS(obj.getKey())); - GetItemResult item = dynamo.getItem(new GetItemRequest() - .withTableName("aws-java-sdk-util-crypto").withKey(key)); - - long start = System.currentTimeMillis(); - for (int i = 0; i < 10000; i++) { - mapper.marshallIntoObject(NumberAttributeTestClass.class, item.getItem()); - } - - long end = System.currentTimeMillis(); - - System.err.println("time: " + (end - start)); + } + + /** Tests automatically setting a hash key upon saving. */ + @Test + public void testSetHashKey() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + NumberAttributeTestClass obj = getUniqueObject(); + obj.setKey(null); + objs.add(obj); } - - private NumberAttributeTestClass getUniqueObject() { - NumberAttributeTestClass obj = new NumberAttributeTestClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); - obj.setByteAttribute((byte)byteStart++); - obj.setByteObjectAttribute(new Byte("" + byteStart++)); - obj.setDoubleAttribute(new Double("" + start++)); - obj.setDoubleObjectAttribute(new Double("" + start++)); - obj.setFloatAttribute(new Float("" + start++)); - obj.setFloatObjectAttribute(new Float("" + start++)); - obj.setIntAttribute(new Integer("" + start++)); - obj.setIntegerAttribute(new Integer("" + start++)); - obj.setLongAttribute(new Long("" + start++)); - obj.setLongObjectAttribute(new Long("" + start++)); - obj.setShortAttribute(new Short("" + start++)); - obj.setShortObjectAttribute(new Short("" + start++)); - obj.setDateAttribute(new Date(startKey++)); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanObjectAttribute(start++ % 2 == 0); - obj.setIgnored("" + start++); - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(new Date(startKey++)); - obj.setCalendarAttribute(cal); - return obj; + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (NumberAttributeTestClass obj : objs) { + assertNull(obj.getKey()); + util.save(obj); + assertNotNull(obj.getKey()); + NumberAttributeTestClass loadedObject = util.load(obj); + + assertFalse(obj.getIgnored().equals(loadedObject.getIgnored())); + loadedObject.setIgnored(obj.getIgnored()); + assertEquals(obj, loadedObject); + } + } + + @Test + public void testDelete() throws Exception { + NumberAttributeTestClass obj = getUniqueObject(); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(obj); + + NumberAttributeTestClass loaded = util.load(NumberAttributeTestClass.class, obj.getKey()); + loaded.setIgnored(obj.getIgnored()); + assertEquals(obj, loaded); + + util.delete(obj); + assertNull(util.load(NumberAttributeTestClass.class, obj.getKey())); + } + + @Test + public void performanceTest() throws Exception { + NumberAttributeTestClass obj = getUniqueObject(); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); + HashMap key = new HashMap(); + key.put(KEY_NAME, new AttributeValue().withS(obj.getKey())); + GetItemResult item = + dynamo.getItem(new GetItemRequest().withTableName("aws-java-sdk-util-crypto").withKey(key)); + + long start = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + mapper.marshallIntoObject(NumberAttributeTestClass.class, item.getItem()); } + long end = System.currentTimeMillis(); + + System.err.println("time: " + (end - start)); + } + private NumberAttributeTestClass getUniqueObject() { + NumberAttributeTestClass obj = new NumberAttributeTestClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setBigDecimalAttribute(new BigDecimal(startKey++)); + obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); + obj.setByteAttribute((byte) byteStart++); + obj.setByteObjectAttribute(new Byte("" + byteStart++)); + obj.setDoubleAttribute(new Double("" + start++)); + obj.setDoubleObjectAttribute(new Double("" + start++)); + obj.setFloatAttribute(new Float("" + start++)); + obj.setFloatObjectAttribute(new Float("" + start++)); + obj.setIntAttribute(new Integer("" + start++)); + obj.setIntegerAttribute(new Integer("" + start++)); + obj.setLongAttribute(new Long("" + start++)); + obj.setLongObjectAttribute(new Long("" + start++)); + obj.setShortAttribute(new Short("" + start++)); + obj.setShortObjectAttribute(new Short("" + start++)); + obj.setDateAttribute(new Date(startKey++)); + obj.setBooleanAttribute(start++ % 2 == 0); + obj.setBooleanObjectAttribute(start++ % 2 == 0); + obj.setIgnored("" + start++); + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(new Date(startKey++)); + obj.setCalendarAttribute(cal); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleStringAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleStringAttributesITCase.java index 60085e63..f39f40bc 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleStringAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/SimpleStringAttributesITCase.java @@ -1,17 +1,20 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; @@ -25,192 +28,182 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - - -/** - * Tests simple string attributes - */ +/** Tests simple string attributes */ public class SimpleStringAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; - private static final String STRING_ATTRIBUTE = "stringAttribute"; - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for ( int i = 0; i < 5; i++ ) { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - attrs.add(attr); - } - }; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperCryptoIntegrationTestBase.setUp(); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() - .withHashKeyName(KEY_NAME) - .withTableName(TABLE_NAME) - .build(); - // Insert the data - for ( Map attr : attrs ) { - attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); - } + private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; + private static final String STRING_ATTRIBUTE = "stringAttribute"; + private static final List> attrs = + new LinkedList>(); + + // Test data + static { + for (int i = 0; i < 5; i++) { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + attrs.add(attr); } - - @Test - public void testLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - for ( Map attr : attrs ) { - StringAttributeTestClass x = util.load(StringAttributeTestClass.class, attr.get(KEY_NAME).getS()); - assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); - assertEquals(x.getStringAttribute(), attr.get(STRING_ATTRIBUTE).getS()); - assertEquals(x.getRenamedAttribute(), attr.get(ORIGINAL_NAME_ATTRIBUTE).getS()); - } - + } + ; + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + // Insert the data + for (Map attr : attrs) { + attr = encryptor.encryptAllFieldsExcept(attr, context, KEY_NAME); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); + } + } + + @Test + public void testLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map attr : attrs) { + StringAttributeTestClass x = + util.load(StringAttributeTestClass.class, attr.get(KEY_NAME).getS()); + assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); + assertEquals(x.getStringAttribute(), attr.get(STRING_ATTRIBUTE).getS()); + assertEquals(x.getRenamedAttribute(), attr.get(ORIGINAL_NAME_ATTRIBUTE).getS()); + } + } + + @Test + public void testSave() { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + StringAttributeTestClass obj = getUniqueObject(); + objs.add(obj); } - @Test - public void testSave() { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - StringAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (StringAttributeTestClass obj : objs) { - util.save(obj); - } - - for (StringAttributeTestClass obj : objs) { - StringAttributeTestClass loaded = util.load(StringAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (StringAttributeTestClass obj : objs) { + util.save(obj); } - - /** - * Tests saving an incomplete object into DynamoDB - */ - @Test - public void testIncompleteObject() { - StringAttributeTestClass obj = getUniqueObject(); - obj.setStringAttribute(null); - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - util.save(obj); - - assertEquals(obj, util.load(StringAttributeTestClass.class, obj.getKey())); - - // test removing an attribute - assertNotNull(obj.getRenamedAttribute()); - obj.setRenamedAttribute(null); - util.save(obj); - assertEquals(obj, util.load(StringAttributeTestClass.class, obj.getKey())); + + for (StringAttributeTestClass obj : objs) { + StringAttributeTestClass loaded = util.load(StringAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); } - - @Test - public void testUpdate() { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - StringAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (StringAttributeTestClass obj : objs) { - util.save(obj); - } - - for ( StringAttributeTestClass obj : objs ) { - StringAttributeTestClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - - assertEquals(replacement, util.load(StringAttributeTestClass.class, obj.getKey())); - } + } + + /** Tests saving an incomplete object into DynamoDB */ + @Test + public void testIncompleteObject() { + StringAttributeTestClass obj = getUniqueObject(); + obj.setStringAttribute(null); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + util.save(obj); + + assertEquals(obj, util.load(StringAttributeTestClass.class, obj.getKey())); + + // test removing an attribute + assertNotNull(obj.getRenamedAttribute()); + obj.setRenamedAttribute(null); + util.save(obj); + assertEquals(obj, util.load(StringAttributeTestClass.class, obj.getKey())); + } + + @Test + public void testUpdate() { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + StringAttributeTestClass obj = getUniqueObject(); + objs.add(obj); } - - @Test - public void testSaveOnlyKey() { - KeyOnly obj = new KeyOnly(); - obj.setKey("" + startKey++); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj); - - KeyOnly loaded = mapper.load(KeyOnly.class, obj.getKey(), new DynamoDBMapperConfig(ConsistentReads.CONSISTENT)); - assertEquals(obj, loaded); - - // saving again shouldn't be an error - mapper.save(obj); + + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (StringAttributeTestClass obj : objs) { + util.save(obj); } - - @Test - public void testSaveOnlyKeyClobber() { - KeyOnly obj = new KeyOnly(); - obj.setKey("" + startKey++); - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - mapper.save(obj, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); - - KeyOnly loaded = mapper.load(KeyOnly.class, obj.getKey(), new DynamoDBMapperConfig(ConsistentReads.CONSISTENT)); - assertEquals(obj, loaded); - - // saving again shouldn't be an error - mapper.save(obj, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); + + for (StringAttributeTestClass obj : objs) { + StringAttributeTestClass replacement = getUniqueObject(); + replacement.setKey(obj.getKey()); + util.save(replacement); + + assertEquals(replacement, util.load(StringAttributeTestClass.class, obj.getKey())); } - - @DynamoDBTable(tableName="aws-java-sdk-util-crypto") - public static final class KeyOnly { - private String key; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - KeyOnly other = (KeyOnly) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - return true; - } + } + + @Test + public void testSaveOnlyKey() { + KeyOnly obj = new KeyOnly(); + obj.setKey("" + startKey++); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj); + + KeyOnly loaded = + mapper.load( + KeyOnly.class, obj.getKey(), new DynamoDBMapperConfig(ConsistentReads.CONSISTENT)); + assertEquals(obj, loaded); + + // saving again shouldn't be an error + mapper.save(obj); + } + + @Test + public void testSaveOnlyKeyClobber() { + KeyOnly obj = new KeyOnly(); + obj.setKey("" + startKey++); + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + mapper.save(obj, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); + + KeyOnly loaded = + mapper.load( + KeyOnly.class, obj.getKey(), new DynamoDBMapperConfig(ConsistentReads.CONSISTENT)); + assertEquals(obj, loaded); + + // saving again shouldn't be an error + mapper.save(obj, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); + } + + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static final class KeyOnly { + private String key; + + @DynamoDBHashKey + public String getKey() { + return key; } - private StringAttributeTestClass getUniqueObject() { - StringAttributeTestClass obj = new StringAttributeTestClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setRenamedAttribute(String.valueOf(startKey++)); - obj.setStringAttribute(String.valueOf(startKey++)); - return obj; + public void setKey(String key) { + this.key = key; } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + KeyOnly other = (KeyOnly) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + return true; + } + } + + private StringAttributeTestClass getUniqueObject() { + StringAttributeTestClass obj = new StringAttributeTestClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setRenamedAttribute(String.valueOf(startKey++)); + obj.setStringAttribute(String.valueOf(startKey++)); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/StringSetAttributesITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/StringSetAttributesITCase.java index 0f7cf71a..e844ae73 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/StringSetAttributesITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/StringSetAttributesITCase.java @@ -1,17 +1,19 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; @@ -21,138 +23,140 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; - - - -/** - * Tests string set attributes - */ +/** Tests string set attributes */ public class StringSetAttributesITCase extends DynamoDBMapperCryptoIntegrationTestBase { - private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; - private static final String STRING_SET_ATTRIBUTE = "stringSetAttribute"; - private static final String EXTRA_ATTRIBUTE = "extra"; - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for ( int i = 0; i < 5; i++ ) { - Map attr = new HashMap(); - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - attr.put(STRING_SET_ATTRIBUTE, new AttributeValue().withSS("" + ++startKey, "" + ++startKey, "" + ++startKey)); - attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withSS("" + ++startKey, "" + ++startKey, "" + ++startKey)); - attr.put(EXTRA_ATTRIBUTE, new AttributeValue().withSS("" + ++startKey, "" + ++startKey, "" + ++startKey)); - attrs.add(attr); - } - }; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperCryptoIntegrationTestBase.setUp(); - DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = new EncryptionContext.Builder() - .withHashKeyName(KEY_NAME) - .withTableName(TABLE_NAME) - .build(); - // Insert the data - for ( Map attr : attrs ) { - Map> flags = encryptor.allEncryptionFlagsExcept(attr, KEY_NAME); - flags.remove(EXTRA_ATTRIBUTE); // exclude "extra" entirely since - // it's not defined in the - // StringSetAttributeTestClass pojo - attr = encryptor.encryptRecord(attr, flags, context); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); - } + private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; + private static final String STRING_SET_ATTRIBUTE = "stringSetAttribute"; + private static final String EXTRA_ATTRIBUTE = "extra"; + private static final List> attrs = + new LinkedList>(); + + // Test data + static { + for (int i = 0; i < 5; i++) { + Map attr = new HashMap(); + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + attr.put( + STRING_SET_ATTRIBUTE, + new AttributeValue().withSS("" + ++startKey, "" + ++startKey, "" + ++startKey)); + attr.put( + ORIGINAL_NAME_ATTRIBUTE, + new AttributeValue().withSS("" + ++startKey, "" + ++startKey, "" + ++startKey)); + attr.put( + EXTRA_ATTRIBUTE, + new AttributeValue().withSS("" + ++startKey, "" + ++startKey, "" + ++startKey)); + attrs.add(attr); + } + } + ; + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + // Insert the data + for (Map attr : attrs) { + Map> flags = encryptor.allEncryptionFlagsExcept(attr, KEY_NAME); + flags.remove(EXTRA_ATTRIBUTE); // exclude "extra" entirely since + // it's not defined in the + // StringSetAttributeTestClass pojo + attr = encryptor.encryptRecord(attr, flags, context); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); } - - @Test - public void testLoad() throws Exception { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - for ( Map attr : attrs ) { - StringSetAttributeTestClass x = util.load(StringSetAttributeTestClass.class, attr.get(KEY_NAME).getS()); - assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); - assertSetsEqual(x.getStringSetAttribute(), toSet(attr.get(STRING_SET_ATTRIBUTE).getSS())); - assertSetsEqual(x.getStringSetAttributeRenamed(), toSet(attr.get(ORIGINAL_NAME_ATTRIBUTE).getSS())); - } + } + + @Test + public void testLoad() throws Exception { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map attr : attrs) { + StringSetAttributeTestClass x = + util.load(StringSetAttributeTestClass.class, attr.get(KEY_NAME).getS()); + assertEquals(x.getKey(), attr.get(KEY_NAME).getS()); + assertSetsEqual(x.getStringSetAttribute(), toSet(attr.get(STRING_SET_ATTRIBUTE).getSS())); + assertSetsEqual( + x.getStringSetAttributeRenamed(), toSet(attr.get(ORIGINAL_NAME_ATTRIBUTE).getSS())); + } + } + + /** Tests saving only some attributes of an object. */ + @Test + public void testIncompleteObject() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + StringSetAttributeTestClass obj = getUniqueObject(); + obj.setStringSetAttribute(null); + util.save(obj); + + assertEquals(obj, util.load(StringSetAttributeTestClass.class, obj.getKey())); + + obj.setStringSetAttributeRenamed(null); + util.save(obj); + assertEquals(obj, util.load(StringSetAttributeTestClass.class, obj.getKey())); + } + + @Test + public void testSave() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + StringSetAttributeTestClass obj = getUniqueObject(); + objs.add(obj); } - /** - * Tests saving only some attributes of an object. - */ - @Test - public void testIncompleteObject() { - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - - StringSetAttributeTestClass obj = getUniqueObject(); - obj.setStringSetAttribute(null); - util.save(obj); - - assertEquals(obj, util.load(StringSetAttributeTestClass.class, obj.getKey())); - - obj.setStringSetAttributeRenamed(null); - util.save(obj); - assertEquals(obj, util.load(StringSetAttributeTestClass.class, obj.getKey())); + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (StringSetAttributeTestClass obj : objs) { + util.save(obj); } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - StringSetAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (StringSetAttributeTestClass obj : objs) { - util.save(obj); - } - - for (StringSetAttributeTestClass obj : objs) { - StringSetAttributeTestClass loaded = util.load(StringSetAttributeTestClass.class, obj.getKey()); - assertEquals(obj, loaded); - } + + for (StringSetAttributeTestClass obj : objs) { + StringSetAttributeTestClass loaded = + util.load(StringSetAttributeTestClass.class, obj.getKey()); + assertEquals(obj, loaded); } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - StringSetAttributeTestClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for (StringSetAttributeTestClass obj : objs) { - util.save(obj); - } - - for ( StringSetAttributeTestClass obj : objs ) { - StringSetAttributeTestClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - - assertEquals(replacement, util.load(StringSetAttributeTestClass.class, obj.getKey())); - } + } + + @Test + public void testUpdate() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + StringSetAttributeTestClass obj = getUniqueObject(); + objs.add(obj); } - private StringSetAttributeTestClass getUniqueObject() { - StringSetAttributeTestClass obj = new StringSetAttributeTestClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setStringSetAttribute(toSet(String.valueOf(startKey++), String.valueOf(startKey++), String.valueOf(startKey++))); - obj.setStringSetAttributeRenamed(toSet(String.valueOf(startKey++), String.valueOf(startKey++), String.valueOf(startKey++))); - return obj; + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (StringSetAttributeTestClass obj : objs) { + util.save(obj); } + for (StringSetAttributeTestClass obj : objs) { + StringSetAttributeTestClass replacement = getUniqueObject(); + replacement.setKey(obj.getKey()); + util.save(replacement); + + assertEquals(replacement, util.load(StringSetAttributeTestClass.class, obj.getKey())); + } + } + + private StringSetAttributeTestClass getUniqueObject() { + StringSetAttributeTestClass obj = new StringSetAttributeTestClass(); + obj.setKey(String.valueOf(startKey++)); + obj.setStringSetAttribute( + toSet(String.valueOf(startKey++), String.valueOf(startKey++), String.valueOf(startKey++))); + obj.setStringSetAttributeRenamed( + toSet(String.valueOf(startKey++), String.valueOf(startKey++), String.valueOf(startKey++))); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/VersionAttributeUpdateITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/VersionAttributeUpdateITCase.java index 6c111ab2..cadcb8cf 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/VersionAttributeUpdateITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/VersionAttributeUpdateITCase.java @@ -1,17 +1,23 @@ /* * Copyright 2015 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.services.dynamodbv2.mapper.integration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; + import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDeleteExpression; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; @@ -28,590 +34,562 @@ import com.amazonaws.services.dynamodbv2.model.ConditionalOperator; import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue; import com.amazonaws.util.ImmutableMapParameter; -import org.testng.annotations.Test; - import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.fail; +/** Tests updating version fields correctly */ +public class VersionAttributeUpdateITCase extends DynamoDBMapperCryptoIntegrationTestBase { + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static class VersionFieldBaseClass { -/** - * Tests updating version fields correctly - */ -public class VersionAttributeUpdateITCase extends DynamoDBMapperCryptoIntegrationTestBase { + protected String key; + protected String normalStringAttribute; + + @DynamoDBHashKey + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAttribute + public String getNormalStringAttribute() { + return normalStringAttribute; + } + + public void setNormalStringAttribute(String normalStringAttribute) { + this.normalStringAttribute = normalStringAttribute; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = + prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + VersionFieldBaseClass other = (VersionFieldBaseClass) obj; + if (key == null) { + if (other.key != null) return false; + } else if (!key.equals(other.key)) return false; + if (normalStringAttribute == null) { + if (other.normalStringAttribute != null) return false; + } else if (!normalStringAttribute.equals(other.normalStringAttribute)) return false; + return true; + } + } + + public static class StringVersionField extends VersionFieldBaseClass { + + private String version; + + @DynamoDBVersionAttribute + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + StringVersionField other = (StringVersionField) obj; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testStringVersion() throws Exception { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + StringVersionField obj = getUniqueObject(new StringVersionField()); + objs.add(obj); + } + + // Saving new objects with a null version field should populate it + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (StringVersionField obj : objs) { + assertNull(obj.getVersion()); + util.save(obj); + assertNotNull(obj.getVersion()); + assertEquals(obj, util.load(StringVersionField.class, obj.getKey())); + } + } + + public static class BigIntegerVersionField extends VersionFieldBaseClass { + + private BigInteger version; + + @DynamoDBVersionAttribute + public BigInteger getVersion() { + return version; + } + + public void setVersion(BigInteger version) { + this.version = version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + BigIntegerVersionField other = (BigIntegerVersionField) obj; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } + + @Override + public String toString() { + return "BigIntegerVersionField [version=" + + version + + ", key=" + + key + + ", normalStringAttribute=" + + normalStringAttribute + + "]"; + } + } + + @Test + public void testBigIntegerVersion() { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + BigIntegerVersionField obj = getUniqueObject(new BigIntegerVersionField()); + objs.add(obj); + } + + // Saving new objects with a null version field should populate it + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (BigIntegerVersionField obj : objs) { + assertNull(obj.getVersion()); + util.save(obj); + assertNotNull(obj.getVersion()); + + assertEquals(obj, util.load(BigIntegerVersionField.class, obj.getKey())); + } - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static class VersionFieldBaseClass { - - protected String key; - protected String normalStringAttribute; - - @DynamoDBHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDBAttribute - public String getNormalStringAttribute() { - return normalStringAttribute; - } - - public void setNormalStringAttribute(String normalStringAttribute) { - this.normalStringAttribute = normalStringAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( obj == null ) - return false; - if ( getClass() != obj.getClass() ) - return false; - VersionFieldBaseClass other = (VersionFieldBaseClass) obj; - if ( key == null ) { - if ( other.key != null ) - return false; - } else if ( !key.equals(other.key) ) - return false; - if ( normalStringAttribute == null ) { - if ( other.normalStringAttribute != null ) - return false; - } else if ( !normalStringAttribute.equals(other.normalStringAttribute) ) - return false; - return true; - } - } - - public static class StringVersionField extends VersionFieldBaseClass { - - private String version; - - @DynamoDBVersionAttribute - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - StringVersionField other = (StringVersionField) obj; - if ( version == null ) { - if ( other.version != null ) - return false; - } else if ( !version.equals(other.version) ) - return false; - return true; - } - } - - @Test(expectedExceptions = DynamoDBMappingException.class) - public void testStringVersion() throws Exception { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - StringVersionField obj = getUniqueObject(new StringVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( StringVersionField obj : objs ) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - assertEquals(obj, util.load(StringVersionField.class, obj.getKey())); - } - } - - public static class BigIntegerVersionField extends VersionFieldBaseClass { - - private BigInteger version; - - @DynamoDBVersionAttribute - public BigInteger getVersion() { - return version; - } - - public void setVersion(BigInteger version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - BigIntegerVersionField other = (BigIntegerVersionField) obj; - if ( version == null ) { - if ( other.version != null ) - return false; - } else if ( !version.equals(other.version) ) - return false; - return true; - } - - @Override - public String toString() { - return "BigIntegerVersionField [version=" + version + ", key=" + key + ", normalStringAttribute=" - + normalStringAttribute + "]"; - } - } - - @Test - public void testBigIntegerVersion() { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - BigIntegerVersionField obj = getUniqueObject(new BigIntegerVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( BigIntegerVersionField obj : objs ) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - - assertEquals(obj, util.load(BigIntegerVersionField.class, obj.getKey())); - } - - for ( BigIntegerVersionField obj : objs ) { - BigIntegerVersionField replacement = getUniqueObject(new BigIntegerVersionField()); - replacement.setKey(obj.getKey()); - replacement.setVersion(obj.getVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getVersion().equals(replacement.getVersion())); - - BigIntegerVersionField loadedObject = util.load(BigIntegerVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - - // Now try again overlaying the correct version number by using a saveExpression - // this should not throw the conditional check failed exception - try { - DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression(); - Map expected = new HashMap(); - ExpectedAttributeValue expectedVersion = new ExpectedAttributeValue() - .withValue(new AttributeValue() - .withN(obj.getVersion().add(BigInteger.valueOf(1)).toString())); - expected.put("version", expectedVersion); - saveExpression.setExpected(expected); - util.save(obj, saveExpression); - } catch ( Exception expected ) { - fail("This should succeed, version was updated."); - } - } - } - - public static final class IntegerVersionField extends VersionFieldBaseClass { - - private Integer notCalledVersion; - - // Making sure that we can substitute attribute names as necessary - @DynamoDBVersionAttribute(attributeName = "version") - public Integer getNotCalledVersion() { - return notCalledVersion; - } - - public void setNotCalledVersion(Integer notCalledVersion) { - this.notCalledVersion = notCalledVersion; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((notCalledVersion == null) ? 0 : notCalledVersion.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - IntegerVersionField other = (IntegerVersionField) obj; - if ( notCalledVersion == null ) { - if ( other.notCalledVersion != null ) - return false; - } else if ( !notCalledVersion.equals(other.notCalledVersion) ) - return false; - return true; - } - } - - @Test - public void testIntegerVersion() { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - IntegerVersionField obj = getUniqueObject(new IntegerVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( IntegerVersionField obj : objs ) { - assertNull(obj.getNotCalledVersion()); - util.save(obj); - assertNotNull(obj.getNotCalledVersion()); - - assertEquals(obj, util.load(IntegerVersionField.class, obj.getKey())); - } - - for ( IntegerVersionField obj : objs ) { - IntegerVersionField replacement = getUniqueObject(new IntegerVersionField()); - replacement.setKey(obj.getKey()); - replacement.setNotCalledVersion(obj.getNotCalledVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getNotCalledVersion().equals(replacement.getNotCalledVersion())); - - IntegerVersionField loadedObject = util.load(IntegerVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - - // Trying to delete the object should also fail - try { - util.delete(obj); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - - // But specifying CLOBBER will allow deletion - util.save(obj, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); - - // Trying to delete with the wrong version should fail - try { - //version is now 2 in db, set object version to 3. - obj.setNotCalledVersion(3); - util.delete(obj); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - - // Now try deleting again overlaying the correct version number by using a deleteExpression - // this should not throw the conditional check failed exception - try { - DynamoDBDeleteExpression deleteExpression = new DynamoDBDeleteExpression(); - Map expected = new HashMap(); - ExpectedAttributeValue expectedVersion = new ExpectedAttributeValue() - .withValue(new AttributeValue() - .withN("2")); //version is still 2 in db - expected.put("version", expectedVersion); - deleteExpression.setExpected(expected); - util.delete(obj, deleteExpression); - } catch ( Exception expected ) { - fail("This should succeed, version was updated."); - } - } - } - - /** - * Tests providing additional expected conditions when saving and deleting - * item with versioned fields. - */ - @Test - public void testVersionedAttributeWithUserProvidedExpectedConditions() { - DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - IntegerVersionField versionedObject = getUniqueObject(new IntegerVersionField()); - assertNull(versionedObject.getNotCalledVersion()); - - // Add additional expected conditions via DynamoDBSaveExpression. - // Expected conditions joined by AND are compatible with the conditions - // for auto-generated keys. - DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression() - .withExpected(Collections.singletonMap( - "otherAttribute", new ExpectedAttributeValue(false))) - .withConditionalOperator(ConditionalOperator.AND); - // The save should succeed since the user provided conditions are joined by AND. - mapper.save(versionedObject, saveExpression); - // The version field should be populated - assertNotNull(versionedObject.getNotCalledVersion()); - IntegerVersionField other = mapper.load(IntegerVersionField.class, versionedObject.getKey()); - assertEquals(other, versionedObject); - - // delete should also work - DynamoDBDeleteExpression deleteExpression = new DynamoDBDeleteExpression() - .withExpected(Collections.singletonMap( - "otherAttribute", new ExpectedAttributeValue(false))) - .withConditionalOperator(ConditionalOperator.AND); - mapper.delete(versionedObject, deleteExpression); - - // Change the conditional operator to OR. - // IllegalArgumentException is expected since the additional expected - // conditions cannot be joined with the conditions for auto-generated - // keys. - saveExpression.setConditionalOperator(ConditionalOperator.OR); - deleteExpression.setConditionalOperator(ConditionalOperator.OR); - try { - mapper.save(getUniqueObject(new IntegerVersionField()), saveExpression); - } catch (IllegalArgumentException expected) {} - try { - mapper.delete(getUniqueObject(new IntegerVersionField()), deleteExpression); - } catch (IllegalArgumentException expected) {} - - // User-provided OR conditions should work if they completely override - // the generated conditions for the version field. - Map goodConditions = - ImmutableMapParameter.of( - "otherAttribute", new ExpectedAttributeValue(false), - "version", new ExpectedAttributeValue(false) - ); - Map badConditions = - ImmutableMapParameter.of( - "otherAttribute", new ExpectedAttributeValue(new AttributeValue("non-existent-value")), - "version", new ExpectedAttributeValue(new AttributeValue().withN("-1")) - ); - - IntegerVersionField newObj = getUniqueObject(new IntegerVersionField()); - saveExpression.setExpected(badConditions); - try { - mapper.save(newObj, saveExpression); - } catch (ConditionalCheckFailedException expected) {} - - saveExpression.setExpected(goodConditions); - mapper.save(newObj, saveExpression); - - deleteExpression.setExpected(badConditions); - try { - mapper.delete(newObj, deleteExpression); - } catch (ConditionalCheckFailedException expected) {} - - deleteExpression.setExpected(goodConditions); - mapper.delete(newObj, deleteExpression); - } - - public static final class ByteVersionField extends VersionFieldBaseClass { - - private Byte version; - - @DynamoDBVersionAttribute - public Byte getVersion() { - return version; - } - - public void setVersion(Byte version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - ByteVersionField other = (ByteVersionField) obj; - if ( version == null ) { - if ( other.version != null ) - return false; - } else if ( !version.equals(other.version) ) - return false; - return true; - } - } - - @Test - public void testByteVersion() { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - ByteVersionField obj = getUniqueObject(new ByteVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( ByteVersionField obj : objs ) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - - assertEquals(obj, util.load(ByteVersionField.class, obj.getKey())); - } - - for ( ByteVersionField obj : objs ) { - ByteVersionField replacement = getUniqueObject(new ByteVersionField()); - replacement.setKey(obj.getKey()); - replacement.setVersion(obj.getVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getVersion().equals(replacement.getVersion())); - - ByteVersionField loadedObject = util.load(ByteVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - } - } - - public static final class LongVersionField extends VersionFieldBaseClass { - - private Long version; - - @DynamoDBVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) - return true; - if ( !super.equals(obj) ) - return false; - if ( getClass() != obj.getClass() ) - return false; - LongVersionField other = (LongVersionField) obj; - if ( version == null ) { - if ( other.version != null ) - return false; - } else if ( !version.equals(other.version) ) - return false; - return true; - } - } - - @Test - public void testLongVersion() { - List objs = new ArrayList(); - for ( int i = 0; i < 5; i++ ) { - LongVersionField obj = getUniqueObject(new LongVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - for ( LongVersionField obj : objs ) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - - assertEquals(obj, util.load(LongVersionField.class, obj.getKey())); - } - - for ( LongVersionField obj : objs ) { - LongVersionField replacement = getUniqueObject(new LongVersionField()); - replacement.setKey(obj.getKey()); - replacement.setVersion(obj.getVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getVersion().equals(replacement.getVersion())); - - LongVersionField loadedObject = util.load(LongVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch ( Exception expected ) { - } - } - } - - private T getUniqueObject(T obj) { - obj.setKey("" + startKey++); - obj.setNormalStringAttribute("" + startKey++); - return obj; + for (BigIntegerVersionField obj : objs) { + BigIntegerVersionField replacement = getUniqueObject(new BigIntegerVersionField()); + replacement.setKey(obj.getKey()); + replacement.setVersion(obj.getVersion()); + + util.save(replacement); + // The version field should have changed in memory + assertFalse(obj.getVersion().equals(replacement.getVersion())); + + BigIntegerVersionField loadedObject = util.load(BigIntegerVersionField.class, obj.getKey()); + assertEquals(replacement, loadedObject); + + // Trying to update the object again should trigger a concurrency + // exception + try { + util.save(obj); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } + + // Now try again overlaying the correct version number by using a saveExpression + // this should not throw the conditional check failed exception + try { + DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression(); + Map expected = + new HashMap(); + ExpectedAttributeValue expectedVersion = + new ExpectedAttributeValue() + .withValue( + new AttributeValue() + .withN(obj.getVersion().add(BigInteger.valueOf(1)).toString())); + expected.put("version", expectedVersion); + saveExpression.setExpected(expected); + util.save(obj, saveExpression); + } catch (Exception expected) { + fail("This should succeed, version was updated."); + } } + } + + public static final class IntegerVersionField extends VersionFieldBaseClass { + + private Integer notCalledVersion; + + // Making sure that we can substitute attribute names as necessary + @DynamoDBVersionAttribute(attributeName = "version") + public Integer getNotCalledVersion() { + return notCalledVersion; + } + + public void setNotCalledVersion(Integer notCalledVersion) { + this.notCalledVersion = notCalledVersion; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((notCalledVersion == null) ? 0 : notCalledVersion.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + IntegerVersionField other = (IntegerVersionField) obj; + if (notCalledVersion == null) { + if (other.notCalledVersion != null) return false; + } else if (!notCalledVersion.equals(other.notCalledVersion)) return false; + return true; + } + } + + @Test + public void testIntegerVersion() { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + IntegerVersionField obj = getUniqueObject(new IntegerVersionField()); + objs.add(obj); + } + + // Saving new objects with a null version field should populate it + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (IntegerVersionField obj : objs) { + assertNull(obj.getNotCalledVersion()); + util.save(obj); + assertNotNull(obj.getNotCalledVersion()); + + assertEquals(obj, util.load(IntegerVersionField.class, obj.getKey())); + } + + for (IntegerVersionField obj : objs) { + IntegerVersionField replacement = getUniqueObject(new IntegerVersionField()); + replacement.setKey(obj.getKey()); + replacement.setNotCalledVersion(obj.getNotCalledVersion()); + + util.save(replacement); + // The version field should have changed in memory + assertFalse(obj.getNotCalledVersion().equals(replacement.getNotCalledVersion())); + + IntegerVersionField loadedObject = util.load(IntegerVersionField.class, obj.getKey()); + assertEquals(replacement, loadedObject); + + // Trying to update the object again should trigger a concurrency + // exception + try { + util.save(obj); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } + + // Trying to delete the object should also fail + try { + util.delete(obj); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } + + // But specifying CLOBBER will allow deletion + util.save(obj, new DynamoDBMapperConfig(SaveBehavior.CLOBBER)); + + // Trying to delete with the wrong version should fail + try { + // version is now 2 in db, set object version to 3. + obj.setNotCalledVersion(3); + util.delete(obj); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } + + // Now try deleting again overlaying the correct version number by using a deleteExpression + // this should not throw the conditional check failed exception + try { + DynamoDBDeleteExpression deleteExpression = new DynamoDBDeleteExpression(); + Map expected = + new HashMap(); + ExpectedAttributeValue expectedVersion = + new ExpectedAttributeValue() + .withValue(new AttributeValue().withN("2")); // version is still 2 in db + expected.put("version", expectedVersion); + deleteExpression.setExpected(expected); + util.delete(obj, deleteExpression); + } catch (Exception expected) { + fail("This should succeed, version was updated."); + } + } + } + + /** + * Tests providing additional expected conditions when saving and deleting item with versioned + * fields. + */ + @Test + public void testVersionedAttributeWithUserProvidedExpectedConditions() { + DynamoDBMapper mapper = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + IntegerVersionField versionedObject = getUniqueObject(new IntegerVersionField()); + assertNull(versionedObject.getNotCalledVersion()); + + // Add additional expected conditions via DynamoDBSaveExpression. + // Expected conditions joined by AND are compatible with the conditions + // for auto-generated keys. + DynamoDBSaveExpression saveExpression = + new DynamoDBSaveExpression() + .withExpected( + Collections.singletonMap("otherAttribute", new ExpectedAttributeValue(false))) + .withConditionalOperator(ConditionalOperator.AND); + // The save should succeed since the user provided conditions are joined by AND. + mapper.save(versionedObject, saveExpression); + // The version field should be populated + assertNotNull(versionedObject.getNotCalledVersion()); + IntegerVersionField other = mapper.load(IntegerVersionField.class, versionedObject.getKey()); + assertEquals(other, versionedObject); + + // delete should also work + DynamoDBDeleteExpression deleteExpression = + new DynamoDBDeleteExpression() + .withExpected( + Collections.singletonMap("otherAttribute", new ExpectedAttributeValue(false))) + .withConditionalOperator(ConditionalOperator.AND); + mapper.delete(versionedObject, deleteExpression); + + // Change the conditional operator to OR. + // IllegalArgumentException is expected since the additional expected + // conditions cannot be joined with the conditions for auto-generated + // keys. + saveExpression.setConditionalOperator(ConditionalOperator.OR); + deleteExpression.setConditionalOperator(ConditionalOperator.OR); + try { + mapper.save(getUniqueObject(new IntegerVersionField()), saveExpression); + } catch (IllegalArgumentException expected) { + } + try { + mapper.delete(getUniqueObject(new IntegerVersionField()), deleteExpression); + } catch (IllegalArgumentException expected) { + } + + // User-provided OR conditions should work if they completely override + // the generated conditions for the version field. + Map goodConditions = + ImmutableMapParameter.of( + "otherAttribute", new ExpectedAttributeValue(false), + "version", new ExpectedAttributeValue(false)); + Map badConditions = + ImmutableMapParameter.of( + "otherAttribute", new ExpectedAttributeValue(new AttributeValue("non-existent-value")), + "version", new ExpectedAttributeValue(new AttributeValue().withN("-1"))); + + IntegerVersionField newObj = getUniqueObject(new IntegerVersionField()); + saveExpression.setExpected(badConditions); + try { + mapper.save(newObj, saveExpression); + } catch (ConditionalCheckFailedException expected) { + } + + saveExpression.setExpected(goodConditions); + mapper.save(newObj, saveExpression); + + deleteExpression.setExpected(badConditions); + try { + mapper.delete(newObj, deleteExpression); + } catch (ConditionalCheckFailedException expected) { + } + + deleteExpression.setExpected(goodConditions); + mapper.delete(newObj, deleteExpression); + } + + public static final class ByteVersionField extends VersionFieldBaseClass { + + private Byte version; + + @DynamoDBVersionAttribute + public Byte getVersion() { + return version; + } + + public void setVersion(Byte version) { + this.version = version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + ByteVersionField other = (ByteVersionField) obj; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } + } + + @Test + public void testByteVersion() { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + ByteVersionField obj = getUniqueObject(new ByteVersionField()); + objs.add(obj); + } + + // Saving new objects with a null version field should populate it + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (ByteVersionField obj : objs) { + assertNull(obj.getVersion()); + util.save(obj); + assertNotNull(obj.getVersion()); + + assertEquals(obj, util.load(ByteVersionField.class, obj.getKey())); + } + + for (ByteVersionField obj : objs) { + ByteVersionField replacement = getUniqueObject(new ByteVersionField()); + replacement.setKey(obj.getKey()); + replacement.setVersion(obj.getVersion()); + + util.save(replacement); + // The version field should have changed in memory + assertFalse(obj.getVersion().equals(replacement.getVersion())); + + ByteVersionField loadedObject = util.load(ByteVersionField.class, obj.getKey()); + assertEquals(replacement, loadedObject); + + // Trying to update the object again should trigger a concurrency + // exception + try { + util.save(obj); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } + } + } + + public static final class LongVersionField extends VersionFieldBaseClass { + + private Long version; + + @DynamoDBVersionAttribute + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + LongVersionField other = (LongVersionField) obj; + if (version == null) { + if (other.version != null) return false; + } else if (!version.equals(other.version)) return false; + return true; + } + } + + @Test + public void testLongVersion() { + List objs = new ArrayList(); + for (int i = 0; i < 5; i++) { + LongVersionField obj = getUniqueObject(new LongVersionField()); + objs.add(obj); + } + + // Saving new objects with a null version field should populate it + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + for (LongVersionField obj : objs) { + assertNull(obj.getVersion()); + util.save(obj); + assertNotNull(obj.getVersion()); + + assertEquals(obj, util.load(LongVersionField.class, obj.getKey())); + } + + for (LongVersionField obj : objs) { + LongVersionField replacement = getUniqueObject(new LongVersionField()); + replacement.setKey(obj.getKey()); + replacement.setVersion(obj.getVersion()); + + util.save(replacement); + // The version field should have changed in memory + assertFalse(obj.getVersion().equals(replacement.getVersion())); + + LongVersionField loadedObject = util.load(LongVersionField.class, obj.getKey()); + assertEquals(replacement, loadedObject); + + // Trying to update the object again should trigger a concurrency + // exception + try { + util.save(obj); + fail("Should have thrown an exception"); + } catch (Exception expected) { + } + } + } + + private T getUniqueObject(T obj) { + obj.setKey("" + startKey++); + obj.setNormalStringAttribute("" + startKey++); + return obj; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttrMatcher.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttrMatcher.java index 8f0d183c..70af2965 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttrMatcher.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttrMatcher.java @@ -15,111 +15,108 @@ package com.amazonaws.services.dynamodbv2.testing; import com.amazonaws.services.dynamodbv2.model.AttributeValue; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; - import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; public class AttrMatcher extends BaseMatcher> { - private final Map expected; - private final boolean invert; + private final Map expected; + private final boolean invert; - public static AttrMatcher invert(Map expected) { - return new AttrMatcher(expected, true); - } + public static AttrMatcher invert(Map expected) { + return new AttrMatcher(expected, true); + } - public static AttrMatcher match(Map expected) { - return new AttrMatcher(expected, false); - } + public static AttrMatcher match(Map expected) { + return new AttrMatcher(expected, false); + } - public AttrMatcher(Map expected, boolean invert) { - this.expected = expected; - this.invert = invert; - } + public AttrMatcher(Map expected, boolean invert) { + this.expected = expected; + this.invert = invert; + } - @Override - public boolean matches(Object item) { - @SuppressWarnings("unchecked") - Map actual = (Map) item; - if (!expected.keySet().equals(actual.keySet())) { - return invert; - } - for (String key : expected.keySet()) { - AttributeValue e = expected.get(key); - AttributeValue a = actual.get(key); - if (!attrEquals(a, e)) { - return invert; - } - } - return !invert; + @Override + public boolean matches(Object item) { + @SuppressWarnings("unchecked") + Map actual = (Map) item; + if (!expected.keySet().equals(actual.keySet())) { + return invert; } + for (String key : expected.keySet()) { + AttributeValue e = expected.get(key); + AttributeValue a = actual.get(key); + if (!attrEquals(a, e)) { + return invert; + } + } + return !invert; + } - public static boolean attrEquals(AttributeValue e, AttributeValue a) { - if (!isEqual(e.getB(), a.getB()) || - !isEqual(e.getBOOL(), a.getBOOL()) || - !isSetEqual(e.getBS(), a.getBS()) || - !isEqual(e.getN(), a.getN()) || - !isSetEqual(e.getNS(), a.getNS()) || - !isEqual(e.getNULL(), a.getNULL()) || - !isEqual(e.getS(), a.getS()) || - !isSetEqual(e.getSS(), a.getSS())) { - return false; - } - // Recursive types need special handling - if (e.getM() == null ^ a.getM() == null) { - return false; - } else if (e.getM() != null) { - if (!e.getM().keySet().equals(a.getM().keySet())) { - return false; - } - for (final String key : e.getM().keySet()) { - if (!attrEquals(e.getM().get(key), a.getM().get(key))) { - return false; - } - } + public static boolean attrEquals(AttributeValue e, AttributeValue a) { + if (!isEqual(e.getB(), a.getB()) + || !isEqual(e.getBOOL(), a.getBOOL()) + || !isSetEqual(e.getBS(), a.getBS()) + || !isEqual(e.getN(), a.getN()) + || !isSetEqual(e.getNS(), a.getNS()) + || !isEqual(e.getNULL(), a.getNULL()) + || !isEqual(e.getS(), a.getS()) + || !isSetEqual(e.getSS(), a.getSS())) { + return false; + } + // Recursive types need special handling + if (e.getM() == null ^ a.getM() == null) { + return false; + } else if (e.getM() != null) { + if (!e.getM().keySet().equals(a.getM().keySet())) { + return false; + } + for (final String key : e.getM().keySet()) { + if (!attrEquals(e.getM().get(key), a.getM().get(key))) { + return false; } - if (e.getL() == null ^ a.getL() == null) { - return false; - } else if (e.getL() != null) { - if (e.getL().size() != a.getL().size()) { - return false; - } - for (int x = 0; x < e.getL().size(); x++) { - if (!attrEquals(e.getL().get(x), a.getL().get(x))) { - return false; - } - } + } + } + if (e.getL() == null ^ a.getL() == null) { + return false; + } else if (e.getL() != null) { + if (e.getL().size() != a.getL().size()) { + return false; + } + for (int x = 0; x < e.getL().size(); x++) { + if (!attrEquals(e.getL().get(x), a.getL().get(x))) { + return false; } - return true; + } } + return true; + } - @Override - public void describeTo(Description description) { - } + @Override + public void describeTo(Description description) {} - private static boolean isEqual(Object o1, Object o2) { - if (o1 == null ^ o2 == null) { - return false; - } - if (o1 == o2) - return true; - return o1.equals(o2); + private static boolean isEqual(Object o1, Object o2) { + if (o1 == null ^ o2 == null) { + return false; } + if (o1 == o2) return true; + return o1.equals(o2); + } - private static boolean isSetEqual(Collection c1, Collection c2) { - if (c1 == null ^ c2 == null) { - return false; - } - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - if (!s1.equals(s2)) { - return false; - } - } - return true; + private static boolean isSetEqual(Collection c1, Collection c2) { + if (c1 == null ^ c2 == null) { + return false; + } + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + if (!s1.equals(s2)) { + return false; + } } + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueDeserializer.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueDeserializer.java index 6ab968d5..c36484b2 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueDeserializer.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueDeserializer.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; - import java.io.IOException; import java.nio.ByteBuffer; import java.util.Iterator; @@ -16,42 +15,44 @@ import java.util.Set; public class AttributeValueDeserializer extends JsonDeserializer { - @Override - public AttributeValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode attribute = jp.getCodec().readTree(jp); - for (Iterator> iter = attribute.fields(); iter.hasNext(); ) { - Map.Entry rawAttribute = iter.next(); - // If there is more than one entry in this map, there is an error with our test data - if (iter.hasNext()) { - throw new IllegalStateException("Attribute value JSON has more than one value mapped."); - } - String typeString = rawAttribute.getKey(); - JsonNode value = rawAttribute.getValue(); - switch (typeString) { - case "S": - return new AttributeValue().withS(value.asText()); - case "B": - ByteBuffer b = ByteBuffer.wrap(java.util.Base64.getDecoder().decode(value.asText())); - return new AttributeValue().withB(b); - case "N": - return new AttributeValue().withN(value.asText()); - case "SS": - final Set stringSet = objectMapper.readValue( - objectMapper.treeAsTokens(value), - new TypeReference>() {} - ); - return new AttributeValue().withSS(stringSet); - case "NS": - final Set numSet = objectMapper.readValue( - objectMapper.treeAsTokens(value), - new TypeReference>() {} - ); - return new AttributeValue().withNS(numSet); - default: - throw new IllegalStateException("DDB JSON type " + typeString + " not implemented for test attribute value deserialization."); - } - } - return null; + @Override + public AttributeValue deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode attribute = jp.getCodec().readTree(jp); + for (Iterator> iter = attribute.fields(); iter.hasNext(); ) { + Map.Entry rawAttribute = iter.next(); + // If there is more than one entry in this map, there is an error with our test data + if (iter.hasNext()) { + throw new IllegalStateException("Attribute value JSON has more than one value mapped."); + } + String typeString = rawAttribute.getKey(); + JsonNode value = rawAttribute.getValue(); + switch (typeString) { + case "S": + return new AttributeValue().withS(value.asText()); + case "B": + ByteBuffer b = ByteBuffer.wrap(java.util.Base64.getDecoder().decode(value.asText())); + return new AttributeValue().withB(b); + case "N": + return new AttributeValue().withN(value.asText()); + case "SS": + final Set stringSet = + objectMapper.readValue( + objectMapper.treeAsTokens(value), new TypeReference>() {}); + return new AttributeValue().withSS(stringSet); + case "NS": + final Set numSet = + objectMapper.readValue( + objectMapper.treeAsTokens(value), new TypeReference>() {}); + return new AttributeValue().withNS(numSet); + default: + throw new IllegalStateException( + "DDB JSON type " + + typeString + + " not implemented for test attribute value deserialization."); + } } + return null; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueMatcher.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueMatcher.java index 91507701..ce93072b 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueMatcher.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueMatcher.java @@ -15,104 +15,100 @@ package com.amazonaws.services.dynamodbv2.testing; import com.amazonaws.services.dynamodbv2.model.AttributeValue; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; - import java.math.BigDecimal; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; public class AttributeValueMatcher extends BaseMatcher { - private final AttributeValue expected; - private final boolean invert; + private final AttributeValue expected; + private final boolean invert; - public static AttributeValueMatcher invert(AttributeValue expected) { - return new AttributeValueMatcher(expected, true); - } + public static AttributeValueMatcher invert(AttributeValue expected) { + return new AttributeValueMatcher(expected, true); + } - public static AttributeValueMatcher match(AttributeValue expected) { - return new AttributeValueMatcher(expected, false); - } + public static AttributeValueMatcher match(AttributeValue expected) { + return new AttributeValueMatcher(expected, false); + } - public AttributeValueMatcher(AttributeValue expected, boolean invert) { - this.expected = expected; - this.invert = invert; - } + public AttributeValueMatcher(AttributeValue expected, boolean invert) { + this.expected = expected; + this.invert = invert; + } - @Override - public boolean matches(Object item) { - AttributeValue other = (AttributeValue) item; - return invert ^ attrEquals(expected, other); - } + @Override + public boolean matches(Object item) { + AttributeValue other = (AttributeValue) item; + return invert ^ attrEquals(expected, other); + } - @Override - public void describeTo(Description description) { - } + @Override + public void describeTo(Description description) {} - public static boolean attrEquals(AttributeValue e, AttributeValue a) { - if (!isEqual(e.getB(), a.getB()) || - !isNumberEqual(e.getN(), a.getN()) || - !isEqual(e.getS(), a.getS()) || - !isEqual(e.getBS(), a.getBS()) || - !isNumberEqual(e.getNS(), a.getNS()) || - !isEqual(e.getSS(), a.getSS())) { - return false; - } - return true; + public static boolean attrEquals(AttributeValue e, AttributeValue a) { + if (!isEqual(e.getB(), a.getB()) + || !isNumberEqual(e.getN(), a.getN()) + || !isEqual(e.getS(), a.getS()) + || !isEqual(e.getBS(), a.getBS()) + || !isNumberEqual(e.getNS(), a.getNS()) + || !isEqual(e.getSS(), a.getSS())) { + return false; } + return true; + } - private static boolean isNumberEqual(String o1, String o2) { - if (o1 == null ^ o2 == null) { - return false; - } - if (o1 == o2) - return true; - BigDecimal d1 = new BigDecimal(o1); - BigDecimal d2 = new BigDecimal(o2); - return d1.equals(d2); + private static boolean isNumberEqual(String o1, String o2) { + if (o1 == null ^ o2 == null) { + return false; } + if (o1 == o2) return true; + BigDecimal d1 = new BigDecimal(o1); + BigDecimal d2 = new BigDecimal(o2); + return d1.equals(d2); + } - private static boolean isEqual(Object o1, Object o2) { - if (o1 == null ^ o2 == null) { - return false; - } - if (o1 == o2) - return true; - return o1.equals(o2); + private static boolean isEqual(Object o1, Object o2) { + if (o1 == null ^ o2 == null) { + return false; } + if (o1 == o2) return true; + return o1.equals(o2); + } - private static boolean isNumberEqual(Collection c1, Collection c2) { - if (c1 == null ^ c2 == null) { - return false; - } - if (c1 != null) { - Set s1 = new HashSet(); - Set s2 = new HashSet(); - for (String s : c1) { - s1.add(new BigDecimal(s)); - } - for (String s : c2) { - s2.add(new BigDecimal(s)); - } - if (!s1.equals(s2)) { - return false; - } - } - return true; + private static boolean isNumberEqual(Collection c1, Collection c2) { + if (c1 == null ^ c2 == null) { + return false; + } + if (c1 != null) { + Set s1 = new HashSet(); + Set s2 = new HashSet(); + for (String s : c1) { + s1.add(new BigDecimal(s)); + } + for (String s : c2) { + s2.add(new BigDecimal(s)); + } + if (!s1.equals(s2)) { + return false; + } } + return true; + } - private static boolean isEqual(Collection c1, Collection c2) { - if (c1 == null ^ c2 == null) { - return false; - } - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - if (!s1.equals(s2)) { - return false; - } - } - return true; + private static boolean isEqual(Collection c1, Collection c2) { + if (c1 == null ^ c2 == null) { + return false; + } + if (c1 != null) { + Set s1 = new HashSet(c1); + Set s2 = new HashSet(c2); + if (!s1.equals(s2)) { + return false; + } } + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueSerializer.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueSerializer.java index b89c08d6..222d12c2 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueSerializer.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/AttributeValueSerializer.java @@ -5,43 +5,43 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; - import java.io.IOException; import java.nio.ByteBuffer; public class AttributeValueSerializer extends JsonSerializer { - @Override - public void serialize(AttributeValue value, JsonGenerator jgen, - SerializerProvider provider) throws IOException { - if (value != null) { - jgen.writeStartObject(); - if (value.getS() != null) { - jgen.writeStringField("S", value.getS()); - } else if (value.getB() != null) { - ByteBuffer valueBytes = value.getB(); - byte[] arr = new byte[valueBytes.remaining()]; - valueBytes.get(arr); - jgen.writeStringField("B", Base64.encodeAsString(arr)); - } else if (value.getN() != null) { - jgen.writeStringField("N", value.getN()); - } else if (value.getSS() != null) { - jgen.writeFieldName("SS"); - jgen.writeStartArray(); - for (String s : value.getSS()) { - jgen.writeString(s); - } - jgen.writeEndArray(); - } else if (value.getNS() != null) { - jgen.writeFieldName("NS"); - jgen.writeStartArray(); - for (String num : value.getNS()) { - jgen.writeString(num); - } - jgen.writeEndArray(); - } else { - throw new IllegalStateException("AttributeValue has no value or type not implemented for serialization."); - } - jgen.writeEndObject(); + @Override + public void serialize(AttributeValue value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + if (value != null) { + jgen.writeStartObject(); + if (value.getS() != null) { + jgen.writeStringField("S", value.getS()); + } else if (value.getB() != null) { + ByteBuffer valueBytes = value.getB(); + byte[] arr = new byte[valueBytes.remaining()]; + valueBytes.get(arr); + jgen.writeStringField("B", Base64.encodeAsString(arr)); + } else if (value.getN() != null) { + jgen.writeStringField("N", value.getN()); + } else if (value.getSS() != null) { + jgen.writeFieldName("SS"); + jgen.writeStartArray(); + for (String s : value.getSS()) { + jgen.writeString(s); + } + jgen.writeEndArray(); + } else if (value.getNS() != null) { + jgen.writeFieldName("NS"); + jgen.writeStartArray(); + for (String num : value.getNS()) { + jgen.writeString(num); } + jgen.writeEndArray(); + } else { + throw new IllegalStateException( + "AttributeValue has no value or type not implemented for serialization."); + } + jgen.writeEndObject(); } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/DdbRecordMatcher.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/DdbRecordMatcher.java index 064dcd38..1bf47131 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/DdbRecordMatcher.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/DdbRecordMatcher.java @@ -15,48 +15,44 @@ package com.amazonaws.services.dynamodbv2.testing; import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import java.util.Map; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; -import java.util.Map; - public class DdbRecordMatcher extends BaseMatcher> { - private final Map expected; - private final boolean invert; - - public static DdbRecordMatcher invert(Map expected) { - return new DdbRecordMatcher(expected, true); - } - - public static DdbRecordMatcher match(Map expected) { - return new DdbRecordMatcher(expected, false); + private final Map expected; + private final boolean invert; + + public static DdbRecordMatcher invert(Map expected) { + return new DdbRecordMatcher(expected, true); + } + + public static DdbRecordMatcher match(Map expected) { + return new DdbRecordMatcher(expected, false); + } + + public DdbRecordMatcher(Map expected, boolean invert) { + this.expected = expected; + this.invert = invert; + } + + @Override + public boolean matches(Object item) { + @SuppressWarnings("unchecked") + Map actual = (Map) item; + if (!expected.keySet().equals(actual.keySet())) { + return invert; } - - public DdbRecordMatcher(Map expected, boolean invert) { - this.expected = expected; - this.invert = invert; - } - - @Override - public boolean matches(Object item) { - @SuppressWarnings("unchecked") - Map actual = (Map) item; - if (!expected.keySet().equals(actual.keySet())) { - return invert; - } - for (String key : expected.keySet()) { - AttributeValue e = expected.get(key); - AttributeValue a = actual.get(key); - if (!AttributeValueMatcher.attrEquals(a, e)) { - return invert; - } - } - return !invert; + for (String key : expected.keySet()) { + AttributeValue e = expected.get(key); + AttributeValue a = actual.get(key); + if (!AttributeValueMatcher.attrEquals(a, e)) { + return invert; + } } + return !invert; + } - @Override - public void describeTo(Description description) { - } - - + @Override + public void describeTo(Description description) {} } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeKMS.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeKMS.java index cbc1cdda..2e336292 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeKMS.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeKMS.java @@ -29,7 +29,6 @@ import com.amazonaws.services.kms.model.InvalidCiphertextException; import com.amazonaws.services.kms.model.KeyMetadata; import com.amazonaws.services.kms.model.KeyUsageType; - import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.Collections; @@ -39,174 +38,179 @@ import java.util.UUID; public class FakeKMS extends AbstractAWSKMS { - private static final SecureRandom rnd = new SecureRandom(); - private static final String ACCOUNT_ID = "01234567890"; - private final Map results_ = new HashMap<>(); - - @Override - public CreateKeyResult createKey() throws AmazonServiceException, AmazonClientException { - return createKey(new CreateKeyRequest()); + private static final SecureRandom rnd = new SecureRandom(); + private static final String ACCOUNT_ID = "01234567890"; + private final Map results_ = new HashMap<>(); + + @Override + public CreateKeyResult createKey() throws AmazonServiceException, AmazonClientException { + return createKey(new CreateKeyRequest()); + } + + @Override + public CreateKeyResult createKey(CreateKeyRequest req) + throws AmazonServiceException, AmazonClientException { + String keyId = UUID.randomUUID().toString(); + String arn = "arn:aws:testing:kms:" + ACCOUNT_ID + ":key/" + keyId; + CreateKeyResult result = new CreateKeyResult(); + result.setKeyMetadata( + new KeyMetadata() + .withAWSAccountId(ACCOUNT_ID) + .withCreationDate(new Date()) + .withDescription(req.getDescription()) + .withEnabled(true) + .withKeyId(keyId) + .withKeyUsage(KeyUsageType.ENCRYPT_DECRYPT) + .withArn(arn)); + return result; + } + + @Override + public DecryptResult decrypt(DecryptRequest req) + throws AmazonServiceException, AmazonClientException { + DecryptResult result = results_.get(new DecryptMapKey(req)); + if (result != null) { + return result; + } else { + throw new InvalidCiphertextException("Invalid Ciphertext"); } - - @Override - public CreateKeyResult createKey(CreateKeyRequest req) throws AmazonServiceException, - AmazonClientException { - String keyId = UUID.randomUUID().toString(); - String arn = "arn:aws:testing:kms:" + ACCOUNT_ID + ":key/" + keyId; - CreateKeyResult result = new CreateKeyResult(); - result.setKeyMetadata(new KeyMetadata().withAWSAccountId(ACCOUNT_ID) - .withCreationDate(new Date()).withDescription(req.getDescription()) - .withEnabled(true).withKeyId(keyId).withKeyUsage(KeyUsageType.ENCRYPT_DECRYPT) - .withArn(arn)); - return result; + } + + @Override + public EncryptResult encrypt(EncryptRequest req) + throws AmazonServiceException, AmazonClientException { + final byte[] cipherText = new byte[512]; + rnd.nextBytes(cipherText); + DecryptResult dec = new DecryptResult(); + dec.withKeyId(req.getKeyId()).withPlaintext(req.getPlaintext().asReadOnlyBuffer()); + ByteBuffer ctBuff = ByteBuffer.wrap(cipherText); + + results_.put(new DecryptMapKey(ctBuff, req.getEncryptionContext()), dec); + + return new EncryptResult().withCiphertextBlob(ctBuff).withKeyId(req.getKeyId()); + } + + @Override + public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest req) + throws AmazonServiceException, AmazonClientException { + byte[] pt; + if (req.getKeySpec() != null) { + if (req.getKeySpec().contains("256")) { + pt = new byte[32]; + } else if (req.getKeySpec().contains("128")) { + pt = new byte[16]; + } else { + throw new UnsupportedOperationException(); + } + } else { + pt = new byte[req.getNumberOfBytes()]; } - - @Override - public DecryptResult decrypt(DecryptRequest req) throws AmazonServiceException, - AmazonClientException { - DecryptResult result = results_.get(new DecryptMapKey(req)); - if (result != null) { - return result; - } else { - throw new InvalidCiphertextException("Invalid Ciphertext"); - } + rnd.nextBytes(pt); + ByteBuffer ptBuff = ByteBuffer.wrap(pt); + EncryptResult encryptResult = + encrypt( + new EncryptRequest() + .withKeyId(req.getKeyId()) + .withPlaintext(ptBuff) + .withEncryptionContext(req.getEncryptionContext())); + return new GenerateDataKeyResult() + .withKeyId(req.getKeyId()) + .withCiphertextBlob(encryptResult.getCiphertextBlob()) + .withPlaintext(ptBuff); + } + + @Override + public GenerateDataKeyWithoutPlaintextResult generateDataKeyWithoutPlaintext( + GenerateDataKeyWithoutPlaintextRequest req) + throws AmazonServiceException, AmazonClientException { + GenerateDataKeyResult generateDataKey = + generateDataKey( + new GenerateDataKeyRequest() + .withEncryptionContext(req.getEncryptionContext()) + .withNumberOfBytes(req.getNumberOfBytes())); + return new GenerateDataKeyWithoutPlaintextResult() + .withCiphertextBlob(generateDataKey.getCiphertextBlob()) + .withKeyId(req.getKeyId()); + } + + @Override + public void setEndpoint(String arg0) throws IllegalArgumentException { + // Do nothing + } + + @Override + public void setRegion(Region arg0) throws IllegalArgumentException { + // Do nothing + } + + @Override + public void shutdown() { + // Do nothing + } + + public void dump() { + System.out.println(results_); + } + + public Map getSingleEc() { + if (results_.size() != 1) { + throw new IllegalStateException("Unexpected number of ciphertexts"); } - - @Override - public EncryptResult encrypt(EncryptRequest req) throws AmazonServiceException, - AmazonClientException { - final byte[] cipherText = new byte[512]; - rnd.nextBytes(cipherText); - DecryptResult dec = new DecryptResult(); - dec.withKeyId(req.getKeyId()).withPlaintext(req.getPlaintext().asReadOnlyBuffer()); - ByteBuffer ctBuff = ByteBuffer.wrap(cipherText); - - results_.put(new DecryptMapKey(ctBuff, req.getEncryptionContext()), dec); - - return new EncryptResult().withCiphertextBlob(ctBuff).withKeyId(req.getKeyId()); + for (final DecryptMapKey k : results_.keySet()) { + return k.ec; } - - @Override - public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest req) - throws AmazonServiceException, AmazonClientException { - byte[] pt; - if (req.getKeySpec() != null) { - if (req.getKeySpec().contains("256")) { - pt = new byte[32]; - } else if (req.getKeySpec().contains("128")) { - pt = new byte[16]; - } else { - throw new UnsupportedOperationException(); - } - } else { - pt = new byte[req.getNumberOfBytes()]; - } - rnd.nextBytes(pt); - ByteBuffer ptBuff = ByteBuffer.wrap(pt); - EncryptResult encryptResult = encrypt(new EncryptRequest().withKeyId(req.getKeyId()) - .withPlaintext(ptBuff).withEncryptionContext(req.getEncryptionContext())); - return new GenerateDataKeyResult().withKeyId(req.getKeyId()) - .withCiphertextBlob(encryptResult.getCiphertextBlob()).withPlaintext(ptBuff); - + throw new IllegalStateException("Unexpected number of ciphertexts"); + } + + private static class DecryptMapKey { + private final ByteBuffer cipherText; + private final Map ec; + + public DecryptMapKey(DecryptRequest req) { + cipherText = req.getCiphertextBlob().asReadOnlyBuffer(); + if (req.getEncryptionContext() != null) { + ec = Collections.unmodifiableMap(new HashMap(req.getEncryptionContext())); + } else { + ec = Collections.emptyMap(); + } } - @Override - public GenerateDataKeyWithoutPlaintextResult generateDataKeyWithoutPlaintext( - GenerateDataKeyWithoutPlaintextRequest req) throws AmazonServiceException, - AmazonClientException { - GenerateDataKeyResult generateDataKey = generateDataKey(new GenerateDataKeyRequest() - .withEncryptionContext(req.getEncryptionContext()).withNumberOfBytes( - req.getNumberOfBytes())); - return new GenerateDataKeyWithoutPlaintextResult().withCiphertextBlob( - generateDataKey.getCiphertextBlob()).withKeyId(req.getKeyId()); + public DecryptMapKey(ByteBuffer ctBuff, Map ec) { + cipherText = ctBuff.asReadOnlyBuffer(); + if (ec != null) { + this.ec = Collections.unmodifiableMap(new HashMap(ec)); + } else { + this.ec = Collections.emptyMap(); + } } @Override - public void setEndpoint(String arg0) throws IllegalArgumentException { - // Do nothing + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((cipherText == null) ? 0 : cipherText.hashCode()); + result = prime * result + ((ec == null) ? 0 : ec.hashCode()); + return result; } @Override - public void setRegion(Region arg0) throws IllegalArgumentException { - // Do nothing + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + DecryptMapKey other = (DecryptMapKey) obj; + if (cipherText == null) { + if (other.cipherText != null) return false; + } else if (!cipherText.equals(other.cipherText)) return false; + if (ec == null) { + if (other.ec != null) return false; + } else if (!ec.equals(other.ec)) return false; + return true; } @Override - public void shutdown() { - // Do nothing - } - - public void dump() { - System.out.println(results_); - } - - public Map getSingleEc() { - if (results_.size() != 1) { - throw new IllegalStateException("Unexpected number of ciphertexts"); - } - for (final DecryptMapKey k : results_.keySet()) { - return k.ec; - } - throw new IllegalStateException("Unexpected number of ciphertexts"); - } - - private static class DecryptMapKey { - private final ByteBuffer cipherText; - private final Map ec; - - public DecryptMapKey(DecryptRequest req) { - cipherText = req.getCiphertextBlob().asReadOnlyBuffer(); - if (req.getEncryptionContext() != null) { - ec = Collections.unmodifiableMap(new HashMap(req - .getEncryptionContext())); - } else { - ec = Collections.emptyMap(); - } - } - - public DecryptMapKey(ByteBuffer ctBuff, Map ec) { - cipherText = ctBuff.asReadOnlyBuffer(); - if (ec != null) { - this.ec = Collections.unmodifiableMap(new HashMap(ec)); - } else { - this.ec = Collections.emptyMap(); - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((cipherText == null) ? 0 : cipherText.hashCode()); - result = prime * result + ((ec == null) ? 0 : ec.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DecryptMapKey other = (DecryptMapKey) obj; - if (cipherText == null) { - if (other.cipherText != null) - return false; - } else if (!cipherText.equals(other.cipherText)) - return false; - if (ec == null) { - if (other.ec != null) - return false; - } else if (!ec.equals(other.ec)) - return false; - return true; - } - - @Override - public String toString() { - return "DecryptMapKey [cipherText=" + cipherText + ", ec=" + ec + "]"; - } + public String toString() { + return "DecryptMapKey [cipherText=" + cipherText + ", ec=" + ec + "]"; } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeParameters.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeParameters.java index d2ca224d..9f2af051 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeParameters.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/FakeParameters.java @@ -17,7 +17,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.AttributeTransformer; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; import com.amazonaws.services.dynamodbv2.model.AttributeValue; - import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -25,92 +24,108 @@ import java.util.Map; public class FakeParameters { - public static AttributeTransformer.Parameters getInstance(Class clazz, - Map attribs, DynamoDBMapperConfig config, String tableName, - String hashKeyName, String rangeKeyName) { - return getInstance(clazz, attribs, config, tableName, hashKeyName, rangeKeyName, false); - } - - public static AttributeTransformer.Parameters getInstance(Class clazz, - Map attribs, DynamoDBMapperConfig config, String tableName, - String hashKeyName, String rangeKeyName, boolean isPartialUpdate) { - - // We use this relatively insane proxy setup so that modifications to the Parameters - // interface doesn't break our tests (unless it actually impacts our code). - FakeParameters fakeParams = new FakeParameters(clazz, attribs, config, tableName, - hashKeyName, rangeKeyName, isPartialUpdate); - @SuppressWarnings("unchecked") - AttributeTransformer.Parameters proxyObject = (AttributeTransformer.Parameters) Proxy - .newProxyInstance(AttributeTransformer.class.getClassLoader(), - new Class[]{AttributeTransformer.Parameters.class}, - new ParametersInvocationHandler(fakeParams)); - return proxyObject; - } - - private static class ParametersInvocationHandler implements InvocationHandler { - private final FakeParameters params; - - public ParametersInvocationHandler(FakeParameters params) { - this.params = params; - } - - @Override - public Object invoke(Object obj, Method method, Object[] args) throws Throwable { - if (args != null && args.length > 0) { - throw new UnsupportedOperationException(); - } - Method innerMethod = params.getClass().getMethod(method.getName()); - return innerMethod.invoke(params); - } - - } - - private final Map attrs; - private final Class clazz; - private final DynamoDBMapperConfig config; - private final String tableName; - private final String hashKeyName; - private final String rangeKeyName; - private final boolean isPartialUpdate; - - private FakeParameters(Class clazz, Map attribs, - DynamoDBMapperConfig config, String tableName, String hashKeyName, String rangeKeyName, - boolean isPartialUpdate) { - super(); - this.clazz = clazz; - this.attrs = Collections.unmodifiableMap(attribs); - this.config = config; - this.tableName = tableName; - this.hashKeyName = hashKeyName; - this.rangeKeyName = rangeKeyName; - this.isPartialUpdate = isPartialUpdate; - } - - public Map getAttributeValues() { - return attrs; - } - - public Class getModelClass() { - return clazz; - } - - public DynamoDBMapperConfig getMapperConfig() { - return config; - } - - public String getTableName() { - return tableName; - } - - public String getHashKeyName() { - return hashKeyName; - } - - public String getRangeKeyName() { - return rangeKeyName; + public static AttributeTransformer.Parameters getInstance( + Class clazz, + Map attribs, + DynamoDBMapperConfig config, + String tableName, + String hashKeyName, + String rangeKeyName) { + return getInstance(clazz, attribs, config, tableName, hashKeyName, rangeKeyName, false); + } + + public static AttributeTransformer.Parameters getInstance( + Class clazz, + Map attribs, + DynamoDBMapperConfig config, + String tableName, + String hashKeyName, + String rangeKeyName, + boolean isPartialUpdate) { + + // We use this relatively insane proxy setup so that modifications to the Parameters + // interface doesn't break our tests (unless it actually impacts our code). + FakeParameters fakeParams = + new FakeParameters( + clazz, attribs, config, tableName, hashKeyName, rangeKeyName, isPartialUpdate); + @SuppressWarnings("unchecked") + AttributeTransformer.Parameters proxyObject = + (AttributeTransformer.Parameters) + Proxy.newProxyInstance( + AttributeTransformer.class.getClassLoader(), + new Class[] {AttributeTransformer.Parameters.class}, + new ParametersInvocationHandler(fakeParams)); + return proxyObject; + } + + private static class ParametersInvocationHandler implements InvocationHandler { + private final FakeParameters params; + + public ParametersInvocationHandler(FakeParameters params) { + this.params = params; } - public boolean isPartialUpdate() { - return isPartialUpdate; + @Override + public Object invoke(Object obj, Method method, Object[] args) throws Throwable { + if (args != null && args.length > 0) { + throw new UnsupportedOperationException(); + } + Method innerMethod = params.getClass().getMethod(method.getName()); + return innerMethod.invoke(params); } + } + + private final Map attrs; + private final Class clazz; + private final DynamoDBMapperConfig config; + private final String tableName; + private final String hashKeyName; + private final String rangeKeyName; + private final boolean isPartialUpdate; + + private FakeParameters( + Class clazz, + Map attribs, + DynamoDBMapperConfig config, + String tableName, + String hashKeyName, + String rangeKeyName, + boolean isPartialUpdate) { + super(); + this.clazz = clazz; + this.attrs = Collections.unmodifiableMap(attribs); + this.config = config; + this.tableName = tableName; + this.hashKeyName = hashKeyName; + this.rangeKeyName = rangeKeyName; + this.isPartialUpdate = isPartialUpdate; + } + + public Map getAttributeValues() { + return attrs; + } + + public Class getModelClass() { + return clazz; + } + + public DynamoDBMapperConfig getMapperConfig() { + return config; + } + + public String getTableName() { + return tableName; + } + + public String getHashKeyName() { + return hashKeyName; + } + + public String getRangeKeyName() { + return rangeKeyName; + } + + public boolean isPartialUpdate() { + return isPartialUpdate; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/ScenarioManifest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/ScenarioManifest.java index fb3f1127..fe7b7a44 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/ScenarioManifest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/ScenarioManifest.java @@ -2,66 +2,76 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) public class ScenarioManifest { - public static final String MOST_RECENT_PROVIDER_NAME = "most_recent"; - public static final String WRAPPED_PROVIDER_NAME = "wrapped"; - public static final String STATIC_PROVIDER_NAME = "static"; - public static final String AWS_KMS_PROVIDER_NAME = "awskms"; - public static final String SYMMETRIC_KEY_TYPE = "symmetric"; - - public List scenarios; - @JsonProperty("keys") - public String keyDataPath; - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Scenario { - @JsonProperty("ciphertext") - public String ciphertextPath; - @JsonProperty("provider") - public String providerName; - public String version; - @JsonProperty("material_name") - public String materialName; - - public Metastore metastore; - public Keys keys; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Metastore { - @JsonProperty("ciphertext") - public String path; - @JsonProperty("table_name") - public String tableName; - @JsonProperty("provider") - public String providerName; - - public Keys keys; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Keys { - @JsonProperty("encrypt") - public String encryptName; - @JsonProperty("sign") - public String signName; - @JsonProperty("decrypt") - public String decryptName; - @JsonProperty("verify") - public String verifyName; - } - - public static class KeyData { - public String material; - public String algorithm; - public String encoding; - @JsonProperty("type") - public String keyType; - public String keyId; - } + public static final String MOST_RECENT_PROVIDER_NAME = "most_recent"; + public static final String WRAPPED_PROVIDER_NAME = "wrapped"; + public static final String STATIC_PROVIDER_NAME = "static"; + public static final String AWS_KMS_PROVIDER_NAME = "awskms"; + public static final String SYMMETRIC_KEY_TYPE = "symmetric"; + + public List scenarios; + + @JsonProperty("keys") + public String keyDataPath; + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Scenario { + @JsonProperty("ciphertext") + public String ciphertextPath; + + @JsonProperty("provider") + public String providerName; + + public String version; + + @JsonProperty("material_name") + public String materialName; + + public Metastore metastore; + public Keys keys; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Metastore { + @JsonProperty("ciphertext") + public String path; + + @JsonProperty("table_name") + public String tableName; + + @JsonProperty("provider") + public String providerName; + + public Keys keys; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Keys { + @JsonProperty("encrypt") + public String encryptName; + + @JsonProperty("sign") + public String signName; + + @JsonProperty("decrypt") + public String decryptName; + + @JsonProperty("verify") + public String verifyName; + } + + public static class KeyData { + public String material; + public String algorithm; + public String encoding; + + @JsonProperty("type") + public String keyType; + + public String keyId; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/TestDelegatedKey.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/TestDelegatedKey.java index bcde3bb2..85b15bcc 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/TestDelegatedKey.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/TestDelegatedKey.java @@ -15,7 +15,12 @@ package com.amazonaws.services.dynamodbv2.testing; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DelegatedKey; - +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -23,105 +28,105 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; public class TestDelegatedKey implements DelegatedKey { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final Key realKey; + private final Key realKey; - public TestDelegatedKey(Key key) { - this.realKey = key; - } + public TestDelegatedKey(Key key) { + this.realKey = key; + } - @Override - public String getAlgorithm() { - return "DELEGATED:" + realKey.getAlgorithm(); - } + @Override + public String getAlgorithm() { + return "DELEGATED:" + realKey.getAlgorithm(); + } - @Override - public byte[] getEncoded() { - return realKey.getEncoded(); - } + @Override + public byte[] getEncoded() { + return realKey.getEncoded(); + } - @Override - public String getFormat() { - return realKey.getFormat(); - } + @Override + public String getFormat() { + return realKey.getFormat(); + } - @Override - public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException { - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.ENCRYPT_MODE, realKey); - byte[] iv = cipher.getIV(); - byte[] result = new byte[cipher.getOutputSize(plainText.length) + iv.length + 1]; - result[0] = (byte) iv.length; - System.arraycopy(iv, 0, result, 1, iv.length); - try { - cipher.doFinal(plainText, 0, plainText.length, result, iv.length + 1); - } catch (ShortBufferException e) { - throw new RuntimeException(e); - } - return result; + @Override + public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm) + throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, + NoSuchAlgorithmException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); + cipher.init(Cipher.ENCRYPT_MODE, realKey); + byte[] iv = cipher.getIV(); + byte[] result = new byte[cipher.getOutputSize(plainText.length) + iv.length + 1]; + result[0] = (byte) iv.length; + System.arraycopy(iv, 0, result, 1, iv.length); + try { + cipher.doFinal(plainText, 0, plainText.length, result, iv.length + 1); + } catch (ShortBufferException e) { + throw new RuntimeException(e); } + return result; + } - @Override - public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidAlgorithmParameterException { - final byte ivLength = cipherText[0]; - IvParameterSpec iv = new IvParameterSpec(cipherText, 1, ivLength); - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.DECRYPT_MODE, realKey, iv); - return cipher.doFinal(cipherText, ivLength + 1, cipherText.length - ivLength - 1); - } + @Override + public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm) + throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, + NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { + final byte ivLength = cipherText[0]; + IvParameterSpec iv = new IvParameterSpec(cipherText, 1, ivLength); + Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); + cipher.init(Cipher.DECRYPT_MODE, realKey, iv); + return cipher.doFinal(cipherText, ivLength + 1, cipherText.length - ivLength - 1); + } - @Override - public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) throws InvalidKeyException, - NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException { - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.WRAP_MODE, realKey); - return cipher.wrap(key); - } + @Override + public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, + IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); + cipher.init(Cipher.WRAP_MODE, realKey); + return cipher.wrap(key); + } - @Override - public Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType, - byte[] additionalAssociatedData, String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException { - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.UNWRAP_MODE, realKey); - return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType); - } + @Override + public Key unwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType, + byte[] additionalAssociatedData, + String algorithm) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { + Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); + cipher.init(Cipher.UNWRAP_MODE, realKey); + return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType); + } - @Override - public byte[] sign(byte[] dataToSign, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException { - Mac mac = Mac.getInstance(extractAlgorithm(algorithm)); - mac.init(realKey); - return mac.doFinal(dataToSign); - } + @Override + public byte[] sign(byte[] dataToSign, String algorithm) + throws NoSuchAlgorithmException, InvalidKeyException { + Mac mac = Mac.getInstance(extractAlgorithm(algorithm)); + mac.init(realKey); + return mac.doFinal(dataToSign); + } - @Override - public boolean verify(byte[] dataToSign, byte[] signature, String algorithm) { - try { - byte[] expected = sign(dataToSign, extractAlgorithm(algorithm)); - return MessageDigest.isEqual(expected, signature); - } catch (GeneralSecurityException ex) { - return false; - } + @Override + public boolean verify(byte[] dataToSign, byte[] signature, String algorithm) { + try { + byte[] expected = sign(dataToSign, extractAlgorithm(algorithm)); + return MessageDigest.isEqual(expected, signature); + } catch (GeneralSecurityException ex) { + return false; } + } - private String extractAlgorithm(String alg) { - if (alg.startsWith(getAlgorithm())) { - return alg.substring(10); - } else { - return alg; - } + private String extractAlgorithm(String alg) { + if (alg.startsWith(getAlgorithm())) { + return alg.substring(10); + } else { + return alg; } + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClass.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClass.java index 0a6a842e..7d8d8168 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClass.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClass.java @@ -18,171 +18,166 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBVersionAttribute; - import java.util.Arrays; import java.util.Set; @DynamoDBTable(tableName = "TableName") public class BaseClass { - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(byteArrayValue); - result = prime * result + hashKey; - result = prime * result + ((intSet == null) ? 0 : intSet.hashCode()); - result = prime * result + intValue; - result = prime * result + rangeKey; - result = prime * result - + ((stringSet == null) ? 0 : stringSet.hashCode()); - result = prime * result - + ((stringValue == null) ? 0 : stringValue.hashCode()); - result = prime * result + Double.valueOf(doubleValue).hashCode(); - result = prime * result - + ((doubleSet == null) ? 0 : doubleSet.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BaseClass other = (BaseClass) obj; - if (!Arrays.equals(byteArrayValue, other.byteArrayValue)) - return false; - if (hashKey != other.hashKey) - return false; - if (intSet == null) { - if (other.intSet != null) - return false; - } else if (!intSet.equals(other.intSet)) - return false; - if (intValue != other.intValue) - return false; - if (rangeKey != other.rangeKey) - return false; - if (stringSet == null) { - if (other.stringSet != null) - return false; - } else if (!stringSet.equals(other.stringSet)) - return false; - if (stringValue == null) { - if (other.stringValue != null) - return false; - } else if (!stringValue.equals(other.stringValue)) - return false; - if (doubleSet == null) { - if (other.doubleSet != null) - return false; - } else if (!doubleSet.equals(other.doubleSet)) - return false; - return true; - } - - private int hashKey; - private int rangeKey; - private String stringValue; - private int intValue; - private byte[] byteArrayValue; - private Set stringSet; - private Set intSet; - private Integer version; - private double doubleValue; - private Set doubleSet; - - @DynamoDBHashKey - public int getHashKey() { - return hashKey; - } - - public void setHashKey(int hashKey) { - this.hashKey = hashKey; - } - - @DynamoDBRangeKey - public int getRangeKey() { - return rangeKey; - } - - public void setRangeKey(int rangeKey) { - this.rangeKey = rangeKey; - } - - public String getStringValue() { - return stringValue; - } - - public void setStringValue(String stringValue) { - this.stringValue = stringValue; - } - - public int getIntValue() { - return intValue; - } - - public void setIntValue(int intValue) { - this.intValue = intValue; - } - - public byte[] getByteArrayValue() { - return byteArrayValue; - } - - public void setByteArrayValue(byte[] byteArrayValue) { - this.byteArrayValue = byteArrayValue; - } - - public Set getStringSet() { - return stringSet; - } - - public void setStringSet(Set stringSet) { - this.stringSet = stringSet; - } - - public Set getIntSet() { - return intSet; - } - - public void setIntSet(Set intSet) { - this.intSet = intSet; - } - - public Set getDoubleSet() { - return doubleSet; - } - - public void setDoubleSet(Set doubleSet) { - this.doubleSet = doubleSet; - } - - public double getDoubleValue() { - return doubleValue; - } - - public void setDoubleValue(double doubleValue) { - this.doubleValue = doubleValue; - } - - @DynamoDBVersionAttribute - public Integer getVersion() { - return version; - } - - public void setVersion(Integer version) { - this.version = version; - } - - @Override - public String toString() { - return "BaseClass [hashKey=" + hashKey + ", rangeKey=" + rangeKey - + ", stringValue=" + stringValue + ", intValue=" + intValue - + ", byteArrayValue=" + Arrays.toString(byteArrayValue) - + ", stringSet=" + stringSet + ", intSet=" + intSet - + ", doubleSet=" + doubleSet + ", version=" + version + "]"; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(byteArrayValue); + result = prime * result + hashKey; + result = prime * result + ((intSet == null) ? 0 : intSet.hashCode()); + result = prime * result + intValue; + result = prime * result + rangeKey; + result = prime * result + ((stringSet == null) ? 0 : stringSet.hashCode()); + result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); + result = prime * result + Double.valueOf(doubleValue).hashCode(); + result = prime * result + ((doubleSet == null) ? 0 : doubleSet.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + BaseClass other = (BaseClass) obj; + if (!Arrays.equals(byteArrayValue, other.byteArrayValue)) return false; + if (hashKey != other.hashKey) return false; + if (intSet == null) { + if (other.intSet != null) return false; + } else if (!intSet.equals(other.intSet)) return false; + if (intValue != other.intValue) return false; + if (rangeKey != other.rangeKey) return false; + if (stringSet == null) { + if (other.stringSet != null) return false; + } else if (!stringSet.equals(other.stringSet)) return false; + if (stringValue == null) { + if (other.stringValue != null) return false; + } else if (!stringValue.equals(other.stringValue)) return false; + if (doubleSet == null) { + if (other.doubleSet != null) return false; + } else if (!doubleSet.equals(other.doubleSet)) return false; + return true; + } + + private int hashKey; + private int rangeKey; + private String stringValue; + private int intValue; + private byte[] byteArrayValue; + private Set stringSet; + private Set intSet; + private Integer version; + private double doubleValue; + private Set doubleSet; + + @DynamoDBHashKey + public int getHashKey() { + return hashKey; + } + + public void setHashKey(int hashKey) { + this.hashKey = hashKey; + } + + @DynamoDBRangeKey + public int getRangeKey() { + return rangeKey; + } + + public void setRangeKey(int rangeKey) { + this.rangeKey = rangeKey; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public int getIntValue() { + return intValue; + } + + public void setIntValue(int intValue) { + this.intValue = intValue; + } + + public byte[] getByteArrayValue() { + return byteArrayValue; + } + + public void setByteArrayValue(byte[] byteArrayValue) { + this.byteArrayValue = byteArrayValue; + } + + public Set getStringSet() { + return stringSet; + } + + public void setStringSet(Set stringSet) { + this.stringSet = stringSet; + } + + public Set getIntSet() { + return intSet; + } + + public void setIntSet(Set intSet) { + this.intSet = intSet; + } + + public Set getDoubleSet() { + return doubleSet; + } + + public void setDoubleSet(Set doubleSet) { + this.doubleSet = doubleSet; + } + + public double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(double doubleValue) { + this.doubleValue = doubleValue; + } + + @DynamoDBVersionAttribute + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + @Override + public String toString() { + return "BaseClass [hashKey=" + + hashKey + + ", rangeKey=" + + rangeKey + + ", stringValue=" + + stringValue + + ", intValue=" + + intValue + + ", byteArrayValue=" + + Arrays.toString(byteArrayValue) + + ", stringSet=" + + stringSet + + ", intSet=" + + intSet + + ", doubleSet=" + + doubleSet + + ", version=" + + version + + "]"; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithNewAttribute.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithNewAttribute.java index a82fa109..2984f86c 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithNewAttribute.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithNewAttribute.java @@ -16,14 +16,13 @@ public class BaseClassWithNewAttribute extends BaseClassWithUnknownAttributeAnnotation { - private String newAttribute; + private String newAttribute; - public String getNewAttribute() { - return newAttribute; - } - - public void setNewAttribute(String newAttribute) { - this.newAttribute = newAttribute; - } + public String getNewAttribute() { + return newAttribute; + } + public void setNewAttribute(String newAttribute) { + this.newAttribute = newAttribute; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithUnknownAttributeAnnotation.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithUnknownAttributeAnnotation.java index 2df054ca..bdc60818 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithUnknownAttributeAnnotation.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/BaseClassWithUnknownAttributeAnnotation.java @@ -17,5 +17,4 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.HandleUnknownAttributes; @HandleUnknownAttributes -public class BaseClassWithUnknownAttributeAnnotation extends BaseClass { -} +public class BaseClassWithUnknownAttributeAnnotation extends BaseClass {} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotEncryptField.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotEncryptField.java index b4074558..cd777d4e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotEncryptField.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotEncryptField.java @@ -20,33 +20,28 @@ @DynamoDBTable(tableName = "TableName") public class DoNotEncryptField extends Mixed { - @DoNotEncrypt - int value; + @DoNotEncrypt int value; - public int getValue() { - return value; - } + public int getValue() { + return value; + } - public void setValue(int value) { - this.value = value; - } + public void setValue(int value) { + this.value = value; + } - @Override - public int hashCode() { - return 31 * super.hashCode() + value; - } + @Override + public int hashCode() { + return 31 * super.hashCode() + value; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DoNotEncryptField other = (DoNotEncryptField) obj; - if (value != other.value) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + DoNotEncryptField other = (DoNotEncryptField) obj; + if (value != other.value) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotTouchField.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotTouchField.java index e69c92e8..cec8988b 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotTouchField.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/DoNotTouchField.java @@ -20,33 +20,28 @@ @DynamoDBTable(tableName = "TableName") public class DoNotTouchField extends Mixed { - @DoNotTouch - int value; + @DoNotTouch int value; - public int getValue() { - return value; - } + public int getValue() { + return value; + } - public void setValue(int value) { - this.value = value; - } + public void setValue(int value) { + this.value = value; + } - @Override - public int hashCode() { - return 31 * super.hashCode() + value; - } + @Override + public int hashCode() { + return 31 * super.hashCode() + value; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DoNotTouchField other = (DoNotTouchField) obj; - if (value != other.value) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + DoNotTouchField other = (DoNotTouchField) obj; + if (value != other.value) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/HashKeyOnly.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/HashKeyOnly.java index 05b83903..2e0a2de6 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/HashKeyOnly.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/HashKeyOnly.java @@ -19,47 +19,40 @@ @DynamoDBTable(tableName = "HashKeyOnly") public class HashKeyOnly { - private String hashKey; - - public HashKeyOnly() { - - } - - public HashKeyOnly(String hashKey) { - this.hashKey = hashKey; - } - - @DynamoDBHashKey - public String getHashKey() { - return hashKey; - } - - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((hashKey == null) ? 0 : hashKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - HashKeyOnly other = (HashKeyOnly) obj; - if (hashKey == null) { - if (other.hashKey != null) - return false; - } else if (!hashKey.equals(other.hashKey)) - return false; - return true; - } + private String hashKey; + + public HashKeyOnly() {} + + public HashKeyOnly(String hashKey) { + this.hashKey = hashKey; + } + + @DynamoDBHashKey + public String getHashKey() { + return hashKey; + } + + public void setHashKey(String hashKey) { + this.hashKey = hashKey; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((hashKey == null) ? 0 : hashKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + HashKeyOnly other = (HashKeyOnly) obj; + if (hashKey == null) { + if (other.hashKey != null) return false; + } else if (!hashKey.equals(other.hashKey)) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/KeysOnly.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/KeysOnly.java index 510eb5b3..b03651be 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/KeysOnly.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/KeysOnly.java @@ -20,57 +20,51 @@ @DynamoDBTable(tableName = "TableName") public class KeysOnly { - private int hashKey; - private int rangeKey; + private int hashKey; + private int rangeKey; - public KeysOnly() { - } + public KeysOnly() {} - public KeysOnly(int hashKey, int rangeKey) { - this.hashKey = hashKey; - this.rangeKey = rangeKey; - } + public KeysOnly(int hashKey, int rangeKey) { + this.hashKey = hashKey; + this.rangeKey = rangeKey; + } - @DynamoDBRangeKey - public int getRangeKey() { - return rangeKey; - } + @DynamoDBRangeKey + public int getRangeKey() { + return rangeKey; + } - public void setRangeKey(int rangeKey) { - this.rangeKey = rangeKey; - } + public void setRangeKey(int rangeKey) { + this.rangeKey = rangeKey; + } - @DynamoDBHashKey - public int getHashKey() { - return hashKey; - } + @DynamoDBHashKey + public int getHashKey() { + return hashKey; + } - public void setHashKey(int hashKey) { - this.hashKey = hashKey; - } + public void setHashKey(int hashKey) { + this.hashKey = hashKey; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + hashKey; - result = prime * result + rangeKey; - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + hashKey; + result = prime * result + rangeKey; + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - KeysOnly other = (KeysOnly) obj; - if (hashKey != other.hashKey) - return false; - if (rangeKey != other.rangeKey) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + KeysOnly other = (KeysOnly) obj; + if (hashKey != other.hashKey) return false; + if (rangeKey != other.rangeKey) return false; + return true; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Mixed.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Mixed.java index b50d8644..7f7b9755 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Mixed.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Mixed.java @@ -16,31 +16,30 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotEncrypt; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; - import java.util.Set; public class Mixed extends BaseClass { - @Override - @DoNotEncrypt - public String getStringValue() { - return super.getStringValue(); - } + @Override + @DoNotEncrypt + public String getStringValue() { + return super.getStringValue(); + } - @Override - @DoNotEncrypt - public double getDoubleValue() { - return super.getDoubleValue(); - } + @Override + @DoNotEncrypt + public double getDoubleValue() { + return super.getDoubleValue(); + } - @Override - @DoNotEncrypt - public Set getDoubleSet() { - return super.getDoubleSet(); - } + @Override + @DoNotEncrypt + public Set getDoubleSet() { + return super.getDoubleSet(); + } - @Override - @DoNotTouch - public int getIntValue() { - return super.getIntValue(); - } + @Override + @DoNotTouch + public int getIntValue() { + return super.getIntValue(); + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnly.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnly.java index 98988a27..a4dc9a68 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnly.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnly.java @@ -17,5 +17,4 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotEncrypt; @DoNotEncrypt -public class SignOnly extends BaseClass { -} +public class SignOnly extends BaseClass {} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotation.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotation.java index 538c0387..373a7d35 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotation.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotation.java @@ -19,5 +19,4 @@ @DoNotEncrypt @HandleUnknownAttributes -public class SignOnlyWithUnknownAttributeAnnotation extends BaseClass { -} +public class SignOnlyWithUnknownAttributeAnnotation extends BaseClass {} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.java index edf172b4..59c0749d 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/SignOnlyWithUnknownAttributeAnnotationWithNewAttribute.java @@ -17,16 +17,16 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotEncrypt; @DoNotEncrypt -public class SignOnlyWithUnknownAttributeAnnotationWithNewAttribute extends SignOnlyWithUnknownAttributeAnnotation { +public class SignOnlyWithUnknownAttributeAnnotationWithNewAttribute + extends SignOnlyWithUnknownAttributeAnnotation { - private String newAttribute; + private String newAttribute; - public String getNewAttribute() { - return newAttribute; - } - - public void setNewAttribute(String newAttribute) { - this.newAttribute = newAttribute; - } + public String getNewAttribute() { + return newAttribute; + } + public void setNewAttribute(String newAttribute) { + this.newAttribute = newAttribute; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/TableOverride.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/TableOverride.java index 24897829..f9e6bfa7 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/TableOverride.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/TableOverride.java @@ -17,5 +17,4 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.TableAadOverride; @TableAadOverride(tableName = "Override") -public class TableOverride extends BaseClass { -} +public class TableOverride extends BaseClass {} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Untouched.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Untouched.java index 4463c9a9..0946dc9e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Untouched.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/Untouched.java @@ -17,5 +17,4 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; @DoNotTouch -public class Untouched extends BaseClass { -} +public class Untouched extends BaseClass {} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithNewAttribute.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithNewAttribute.java index 13a6e841..0eab129b 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithNewAttribute.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithNewAttribute.java @@ -19,14 +19,13 @@ @DoNotTouch public class UntouchedWithNewAttribute extends Untouched { - private String newAttribute; + private String newAttribute; - public String getNewAttribute() { - return newAttribute; - } - - public void setNewAttribute(String newAttribute) { - this.newAttribute = newAttribute; - } + public String getNewAttribute() { + return newAttribute; + } + public void setNewAttribute(String newAttribute) { + this.newAttribute = newAttribute; + } } diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotation.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotation.java index 00d36fc4..21c07dcb 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotation.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotation.java @@ -19,5 +19,4 @@ @DoNotTouch @HandleUnknownAttributes -public class UntouchedWithUnknownAttributeAnnotation extends BaseClass { -} +public class UntouchedWithUnknownAttributeAnnotation extends BaseClass {} diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotationWithNewAttribute.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotationWithNewAttribute.java index b4dca9d8..10d24faa 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotationWithNewAttribute.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/testing/types/UntouchedWithUnknownAttributeAnnotationWithNewAttribute.java @@ -17,16 +17,16 @@ import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; @DoNotTouch -public class UntouchedWithUnknownAttributeAnnotationWithNewAttribute extends UntouchedWithUnknownAttributeAnnotation { +public class UntouchedWithUnknownAttributeAnnotationWithNewAttribute + extends UntouchedWithUnknownAttributeAnnotation { - private String newAttribute; + private String newAttribute; - public String getNewAttribute() { - return newAttribute; - } - - public void setNewAttribute(String newAttribute) { - this.newAttribute = newAttribute; - } + public String getNewAttribute() { + return newAttribute; + } + public void setNewAttribute(String newAttribute) { + this.newAttribute = newAttribute; + } } From 31fd16cfd2d2a8d71bef4ba4b6c0697d362ec643 Mon Sep 17 00:00:00 2001 From: Ben Farley <47006790+farleyb-amazon@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:50:14 -0600 Subject: [PATCH 009/105] chore: Add support policy (#149) --- README.md | 2 ++ SUPPORT_POLICY.rst | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 SUPPORT_POLICY.rst diff --git a/README.md b/README.md index 22d00cde..75318a09 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ For more advanced use cases where tighter control over the encryption and signin [Security issue notifications](./CONTRIBUTING.md#security-issue-notifications) +See [Support Policy](./SUPPORT_POLICY.rst) for for details on the current support status of all major versions of this library. + ## Getting Started ### Required Prerequisites diff --git a/SUPPORT_POLICY.rst b/SUPPORT_POLICY.rst new file mode 100644 index 00000000..5f098dea --- /dev/null +++ b/SUPPORT_POLICY.rst @@ -0,0 +1,33 @@ +Overview +======== +This page describes the support policy for the AWS DynamoDB Encryption Client. We regularly provide the AWS DynamoDB Encryption Client with updates that may contain support for new or updated APIs, new features, enhancements, bug fixes, security patches, or documentation updates. Updates may also address changes with dependencies, language runtimes, and operating systems. + +We recommend users to stay up-to-date with DynamoDB Encryption Client releases to keep up with the latest features, security updates, and underlying dependencies. Continued use of an unsupported SDK version is not recommended and is done at the user’s discretion. + + +Major Version Lifecycle +======================== +The AWS DynamoDB Encryption Client follows the same major version lifecycle as the AWS SDK. For details on this lifecycle, see `AWS SDKs and Tools Maintenance Policy`_. + +Version Support Matrix +====================== +This table describes the current support status of each major version of the AWS DynamoDB Encryption Client for Java. It also shows the next status each major version will transition to, and the date at which that transition will happen. + +.. list-table:: + :widths: 30 50 50 50 + :header-rows: 1 + + * - Major version + - Current status + - Next status + - Next status date + * - 1.x + - Maintenance + - End of Support + - 2022-07-08 + * - 2.x + - Generally Available + - + - + +.. _AWS SDKs and Tools Maintenance Policy: https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html#version-life-cycle From 86d821eb67f3277178c258c30eb97aebb678ddf1 Mon Sep 17 00:00:00 2001 From: Tony Knapp <5892063+texastony@users.noreply.github.com> Date: Mon, 26 Jul 2021 10:47:05 -0700 Subject: [PATCH 010/105] ci: create release builds that validate on multiple JDKs (#148) --- codebuild/release/release-prod.yml | 86 ++++++++++++++++++++++++ codebuild/release/release-staging.yml | 91 ++++++++++++++++++++++++++ codebuild/release/settings.xml | 31 +++++++++ codebuild/release/validate-prod.yml | 18 +++++ codebuild/release/validate-staging.yml | 35 ++++++++++ examples/pom.xml | 9 ++- 6 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 codebuild/release/release-prod.yml create mode 100644 codebuild/release/release-staging.yml create mode 100644 codebuild/release/settings.xml create mode 100644 codebuild/release/validate-prod.yml create mode 100644 codebuild/release/validate-staging.yml diff --git a/codebuild/release/release-prod.yml b/codebuild/release/release-prod.yml new file mode 100644 index 00000000..bc25225b --- /dev/null +++ b/codebuild/release/release-prod.yml @@ -0,0 +1,86 @@ +version: 0.2 +# 'release-prod' is an AWS::CodeBuild::BuildSpec that Releases to Sonatype and then validates the release with 'validate-prod' +# Command to trigger this codebuild from an authorized command line. +# aws codebuild start-build-batch \ +# --region us-west-2 \ +# --project-name java-ddb-ec-release \ +# --environment-variables-override name=VERSION,value=,type=PLAINTEXT name=COMMIT_ID,value=,type=PLAINTEXT" + +env: + secrets-manager: + GPG_KEY: Maven-GPG-Keys-Credentials:Keyname + GPG_PASS: Maven-GPG-Keys-Credentials:Passphrase + SONA_USERNAME: Sonatype-Team-Account:Username + SONA_PASSWORD: Sonatype-Team-Account:Password + +phases: + install: + runtime-versions: + java: openjdk8 + pre_build: + commands: + - git checkout $COMMIT_ID + - FOUND_VERSION=$(grep version sdk1/pom.xml | head -n 2 | sed -n 's/[ \t]*\(.*\)<\/version>/\1/p') + - | + if expr ${FOUND_VERSION} != ${VERSION}; then + echo "pom.xml version (${FOUND_VERSION}) does not match expected version (${VERSION}), stopping" + exit 1; + fi + - export SETTINGS_FILE=$(pwd)/codebuild/release/settings.xml + - aws secretsmanager get-secret-value --region us-west-2 --secret-id Maven-GPG-Keys --query SecretBinary --output text | base64 -d > ~/mvn_gpg.tgz + - tar -xvf ~/mvn_gpg.tgz -C ~ + build: + commands: + - | + mvn deploy \ + -Ppublishing \ + -DperformRelease \ + -Dgpg.homedir="$HOME/mvn_gpg" \ + -DautoReleaseAfterClose=true \ + -Dgpg.keyname="$GPG_KEY" \ + -Dgpg.passphrase="$GPG_PASS" \ + -Dsonatype.username="$SONA_USERNAME" \ + -Dsonatype.password="$SONA_PASSWORD" \ + -s $SETTINGS_FILE + + +batch: + fast-fail: false + build-graph: + - identifier: release_to_prod + - identifier: validate_prod_release_openjdk8 + depend-on: + - release_to_prod + buildspec: codebuild/release/validate-prod.yml + env: + variables: + JAVA_ENV_VERSION: openjdk8 + JAVA_NUMERIC_VERSION: 8 + image: aws/codebuild/standard:3.0 + - identifier: validate_prod_release_openjdk11 + depend-on: + - release_to_prod + buildspec: codebuild/release/validate-prod.yml + env: + variables: + JAVA_ENV_VERSION: openjdk11 + JAVA_NUMERIC_VERSION: 11 + image: aws/codebuild/standard:3.0 + - identifier: validate_prod_release_corretto8 + depend-on: + - release_to_prod + buildspec: codebuild/release/validate-prod.yml + env: + variables: + JAVA_ENV_VERSION: corretto8 + JAVA_NUMERIC_VERSION: 8 + image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 + - identifier: validate_prod_release_corretto11 + depend-on: + - release_to_prod + buildspec: codebuild/release/validate-prod.yml + env: + variables: + JAVA_ENV_VERSION: corretto11 + JAVA_NUMERIC_VERSION: 11 + image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 diff --git a/codebuild/release/release-staging.yml b/codebuild/release/release-staging.yml new file mode 100644 index 00000000..be1f68e2 --- /dev/null +++ b/codebuild/release/release-staging.yml @@ -0,0 +1,91 @@ +version: 0.2 +# 'release-staging' is an AWS::CodeBuild::BuildSpec that Stages a release on CodeArtifact and validates the staging using 'validate-staging' +# Command to trigger this codebuild from an authorized command line. +# aws codebuild start-build-batch \ +# --region us-west-2 \ +# --project-name java-ddb-ec-test-release \ +# --environment-variables-override name=VERSION,value=,type=PLAINTEXT name=COMMIT_ID,value=,type=PLAINTEXT + +env: + variables: + REGION: us-east-1 + DOMAIN: crypto-tools-internal + REPOSITORY: java-ddbec-staging + parameter-store: + ACCOUNT: /CodeBuild/AccountId + secrets-manager: + GPG_KEY: Maven-GPG-Keys-Credentials:Keyname + GPG_PASS: Maven-GPG-Keys-Credentials:Passphrase + +phases: + install: + runtime-versions: + java: openjdk8 + pre_build: + commands: + - git checkout $COMMIT_ID + - FOUND_VERSION=$(grep version sdk1/pom.xml | head -n 2 | sed -n 's/[ \t]*\(.*\)<\/version>/\1/p') + - | + if expr ${FOUND_VERSION} != ${VERSION}; then + echo "pom.xml version (${FOUND_VERSION}) does not match expected version (${VERSION}), stopping" + exit 1; + fi + - export SETTINGS_FILE=$(pwd)/codebuild/release/settings.xml + - export CODEARTIFACT_TOKEN=$(aws codeartifact get-authorization-token --domain $DOMAIN --domain-owner $ACCOUNT --query authorizationToken --output text --region ${REGION}) + - export CODEARTIFACT_REPO_URL=https://${DOMAIN}-${ACCOUNT}.d.codeartifact.${REGION}.amazonaws.com/maven/${REPOSITORY} + - aws secretsmanager get-secret-value --region us-west-2 --secret-id Maven-GPG-Keys --query SecretBinary --output text | base64 -d > ~/mvn_gpg.tgz + - tar -xvf ~/mvn_gpg.tgz -C ~ + build: + commands: + - | + mvn deploy \ + -PpublishingCodeArtifact \ + -DperformRelease \ + -Dgpg.homedir="$HOME/mvn_gpg" \ + -DautoReleaseAfterClose=true \ + -Dgpg.keyname="$GPG_KEY" \ + -Dgpg.passphrase="$GPG_PASS" \ + -Dcodeartifact.token=$CODEARTIFACT_TOKEN \ + -DaltDeploymentRepository=codeartifact::default::$CODEARTIFACT_REPO_URL \ + -s $SETTINGS_FILE + +batch: + fast-fail: false + build-graph: + - identifier: release_to_staging + - identifier: validate_staging_release_openjdk8 + depend-on: + - release_to_staging + buildspec: codebuild/release/validate-staging.yml + env: + variables: + JAVA_ENV_VERSION: openjdk8 + JAVA_NUMERIC_VERSION: 8 + image: aws/codebuild/standard:3.0 + - identifier: validate_staging_release_openjdk11 + depend-on: + - release_to_staging + buildspec: codebuild/release/validate-staging.yml + env: + variables: + JAVA_ENV_VERSION: openjdk11 + JAVA_NUMERIC_VERSION: 11 + image: aws/codebuild/standard:3.0 + - identifier: validate_staging_release_corretto8 + depend-on: + - release_to_staging + buildspec: codebuild/release/validate-staging.yml + env: + variables: + JAVA_ENV_VERSION: corretto8 + JAVA_NUMERIC_VERSION: 8 + image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 + - identifier: validate_staging_release_corretto11 + depend-on: + - release_to_staging + buildspec: codebuild/release/validate-staging.yml + env: + variables: + JAVA_ENV_VERSION: corretto11 + JAVA_NUMERIC_VERSION: 11 + image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 diff --git a/codebuild/release/settings.xml b/codebuild/release/settings.xml new file mode 100644 index 00000000..19587de0 --- /dev/null +++ b/codebuild/release/settings.xml @@ -0,0 +1,31 @@ + + + + codeartifact + aws + ${codeartifact.token} + + + sonatype-nexus-staging + ${sonatype.username} + ${sonatype.password} + + + + + + codeartifact + + + codeartifact + codeartifact + ${codeartifact.url} + + + + + + diff --git a/codebuild/release/validate-prod.yml b/codebuild/release/validate-prod.yml new file mode 100644 index 00000000..b43a2638 --- /dev/null +++ b/codebuild/release/validate-prod.yml @@ -0,0 +1,18 @@ +version: 0.2 +# 'validate-prod' is an AWS::CodeBuild::BuildSpec that validates a Sonatype release against the examples + +phases: + install: + runtime-versions: + java: $JAVA_ENV_VERSION + pre_build: + commands: + - cd examples + build: + commands: + - | + mvn verify \ + -Dcheckstyle.skip \ + -Dddbec.version=$VERSION \ + -Dmaven.compiler.target=$JAVA_NUMERIC_VERSION \ + -Dmaven.compiler.source=$JAVA_NUMERIC_VERSION diff --git a/codebuild/release/validate-staging.yml b/codebuild/release/validate-staging.yml new file mode 100644 index 00000000..f967acf5 --- /dev/null +++ b/codebuild/release/validate-staging.yml @@ -0,0 +1,35 @@ +version: 0.2 +# 'validate-staging' is an AWS::CodeBuild::BuildSpec that validates artifacts from CodeArtifact against the examples + +env: + variables: + REGION: us-east-1 + DOMAIN: crypto-tools-internal + REPOSITORY: java-ddbec-staging + parameter-store: + ACCOUNT: /CodeBuild/AccountId + +phases: + install: + commands: + - pip install awscli + runtime-versions: + java: $JAVA_ENV_VERSION + pre_build: + commands: + - export SETTINGS_FILE=$(pwd)/codebuild/release/settings.xml + - export CODEARTIFACT_TOKEN=$(aws codeartifact get-authorization-token --domain $DOMAIN --domain-owner $ACCOUNT --query authorizationToken --output text --region ${REGION}) + - export CODEARTIFACT_REPO_URL=https://${DOMAIN}-${ACCOUNT}.d.codeartifact.${REGION}.amazonaws.com/maven/${REPOSITORY} + - cd examples + build: + commands: + - | + mvn verify \ + -Pcodeartifact \ + -Dcheckstyle.skip \ + -Dddbec.version=$VERSION \ + -Dmaven.compiler.target=$JAVA_NUMERIC_VERSION \ + -Dmaven.compiler.source=$JAVA_NUMERIC_VERSION \ + -Dcodeartifact.token=$CODEARTIFACT_TOKEN \ + -Dcodeartifact.url=$CODEARTIFACT_REPO_URL \ + -s $SETTINGS_FILE diff --git a/examples/pom.xml b/examples/pom.xml index ed2d7775..4f8bc3c9 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,13 +21,16 @@ 1.0.392 3.0.0-M3 3.0.0-M3 + 8 + 8 + 2.0.1 com.amazonaws aws-dynamodb-encryption-java - 2.0.1 + ${ddbec.version} @@ -91,10 +94,6 @@ org.apache.maven.plugins maven-compiler-plugin 3.8.0 - - 1.8 - 1.8 - From ffe233bf9f5ce603e02e8d7ff4d45ff33a31c359 Mon Sep 17 00:00:00 2001 From: bhuvangu Date: Fri, 20 Aug 2021 23:11:49 +0530 Subject: [PATCH 011/105] fix: Allow reading old plaintext records after adding new encrypted field to schema (#152) * fix : NPE if reading record without a signature fields * Fix code formatting * Add integration test for missing signature fields in document. * Updated decrypt logic to be less nested. Improve unit tests * Add IT test for missing "MaterialDesc" fields. * Fix code format. * Remove unwanted if condition and add better unit tests. * Rename Test class to better represent the intent. * Fix Unit test * Remove `if` condition which throw a new error. * Rename IT class to be better * Rename unit test method to be better * Correct formatting Co-authored-by: Bhuvan Gupta --- .../encryption/DynamoDBEncryptor.java | 9 +- .../encryption/DynamoDBEncryptorTest.java | 29 +++++ .../integration/PlaintextItemITCase.java | 115 ++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 5aa5a718..3a40c1d4 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -234,7 +234,7 @@ public Map decryptRecord( Map> attributeFlags, EncryptionContext context) throws GeneralSecurityException { - if (attributeFlags.isEmpty()) { + if (!itemContainsFieldsToDecryptOrSign(itemAttributes.keySet(), attributeFlags)) { return itemAttributes; } // Copy to avoid changing anyone elses objects @@ -291,6 +291,13 @@ public Map decryptRecord( return itemAttributes; } + private boolean itemContainsFieldsToDecryptOrSign( + Set attributeNamesToCheck, Map> attributeFlags) { + return attributeNamesToCheck.stream() + .filter(attributeFlags::containsKey) + .anyMatch(attributeName -> !attributeFlags.get(attributeName).isEmpty()); + } + /** * Returns the encrypted (and signed) record, which is a map of item attributes. There is no side * effect on the input parameters upon calling this method. diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index 6c219e52..6ec4aefd 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -15,6 +15,7 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableName; +import static java.util.stream.Collectors.toMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; @@ -23,6 +24,7 @@ import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; +import static org.testng.collections.Sets.newHashSet; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; @@ -54,6 +56,7 @@ import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; +import org.mockito.internal.util.collections.Sets; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; @@ -498,6 +501,32 @@ public void toByteArray() throws ReflectiveOperationException { assertToByteArray("Direct", expected, buff); } + @Test + public void testDecryptWithPlaintextItem() throws GeneralSecurityException { + Map> attributeWithEmptyEncryptionFlags = + attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet())); + + Map decryptedAttributes = + encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + + /* + Test decrypt with a map that contains a new key (not included in attribs) with an encryption flag set that contains ENCRYPT and SIGN. + */ + @Test + public void testDecryptWithPlainTextItemAndAdditionNewAttributeHavingEncryptionFlag() + throws GeneralSecurityException { + Map> attributeWithEmptyEncryptionFlags = + attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet())); + attributeWithEmptyEncryptionFlags.put( + "newAttribute", Sets.newSet(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN)); + + Map decryptedAttributes = + encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + private void assertToByteArray( final String msg, final byte[] expected, final ByteBuffer testValue) throws ReflectiveOperationException { diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java new file mode 100644 index 00000000..8ff61482 --- /dev/null +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java @@ -0,0 +1,115 @@ +/* + * Copyright 2015 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.services.dynamodbv2.mapper.integration; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; +import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.PutItemRequest; +import java.util.HashMap; +import java.util.Map; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class PlaintextItemITCase extends DynamoDBMapperCryptoIntegrationTestBase { + private static final String STRING_ATTRIBUTE = "stringAttribute"; + private static Map plaintextItem = new HashMap<>(); + // Test data + static { + plaintextItem.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + plaintextItem.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + } + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + // Insert the data + dynamo.putItem(new PutItemRequest(TABLE_NAME, plaintextItem)); + } + + @Test + public void testLoadWithPlaintextItem() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + UntouchedTable load = util.load(UntouchedTable.class, plaintextItem.get(KEY_NAME).getS()); + + assertEquals(load.getKey(), plaintextItem.get(KEY_NAME).getS()); + assertEquals(load.getStringAttribute(), plaintextItem.get(STRING_ATTRIBUTE).getS()); + } + + @Test + public void testLoadWithPlaintextItemWithModelHavingNewEncryptedAttribute() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + UntouchedWithNewEncryptedAttributeTable load = + util.load( + UntouchedWithNewEncryptedAttributeTable.class, plaintextItem.get(KEY_NAME).getS()); + + assertEquals(load.getKey(), plaintextItem.get(KEY_NAME).getS()); + assertEquals(load.getStringAttribute(), plaintextItem.get(STRING_ATTRIBUTE).getS()); + assertNull(load.getNewAttribute()); + } + + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static class UntouchedTable { + + private String key; + + private String stringAttribute; + + @DynamoDBHashKey + @DoNotTouch + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAttribute + @DoNotTouch + public String getStringAttribute() { + return stringAttribute; + } + + public void setStringAttribute(String stringAttribute) { + this.stringAttribute = stringAttribute; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UntouchedTable that = (UntouchedTable) o; + return key.equals(that.key) && stringAttribute.equals(that.stringAttribute); + } + } + + public static final class UntouchedWithNewEncryptedAttributeTable extends UntouchedTable { + private String newAttribute; + + public String getNewAttribute() { + return newAttribute; + } + + public void setNewAttribute(String newAttribute) { + this.newAttribute = newAttribute; + } + } +} From c82d596d8093421a612688d59315cd09e065260e Mon Sep 17 00:00:00 2001 From: lavaleri <49660121+lavaleri@users.noreply.github.com> Date: Fri, 20 Aug 2021 17:11:06 -0700 Subject: [PATCH 012/105] chore: Upgrade sdk to 1.12 (#154) --- sdk1/pom.xml | 6 +++--- .../dynamodbv2/datamodeling/TransformerHolisticIT.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 8620ff72..e0902831 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.11.460 + 1.12.51 pom import @@ -236,14 +236,14 @@ com.fasterxml.jackson.core jackson-annotations - 2.9.0 + 2.12.4 test com.fasterxml.jackson.core jackson-core - 2.9.8 + 2.12.4 test diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java index a60a9f66..9437fd80 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java @@ -882,7 +882,7 @@ private static T getManifestFromFile(String filename, TypeReference typeRef) } final File manifestFile = new File(url.getPath()); final ObjectMapper manifestMapper = new ObjectMapper(); - return manifestMapper.readValue(manifestFile, typeRef); + return (T) manifestMapper.readValue(manifestFile, typeRef); } private static void loadKeyData(String filename) throws IOException { From 286eeb2e5b3af07cff00d5a86c904bc17c48a734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Corella?= <39066999+josecorella@users.noreply.github.com> Date: Tue, 31 Aug 2021 11:06:39 -0700 Subject: [PATCH 013/105] bump version, update CHANGELOG for v2.0.2 (#155) * bump version, update CHANGELOG for v2.0.2 * bump version, update CHANGELOG for v2.0.2 * Updating README and pom.xml to reflect version change * Fixing pom.xml in examples in order to match project pom.xml * Cleaning up changelog * formatting changelog --- CHANGELOG.md | 25 +++++++++++++++++++++++++ README.md | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 148194f9..6ad81a54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,29 @@ # Changelog +## 2.0.2 -- 2021-08-31 +### Fixes +* fix: Allow reading old plaintext records after adding new encrypted field to schema +([#152](https://github.com/aws/aws-dynamodb-encryption-java/pull/152)) +* fix: Update user agent string to correctly reflect version +([#141](https://github.com/aws/aws-dynamodb-encryption-java/pull/141)) +* fix: Data model migration doc breadcrumb. +([#139](https://github.com/aws/aws-dynamodb-encryption-java/pull/139)) + +### Maintenance +* chore: bump aws-java-sdk-bom from 1.11.460 to 1.12.51 +([#154](https://github.com/aws/aws-dynamodb-encryption-java/pull/154)) +* chore: Add support policy +([#149](https://github.com/aws/aws-dynamodb-encryption-java/pull/149)) +* CI: Create release builds that validate on multiple JDKs +([#148](https://github.com/aws/aws-dynamodb-encryption-java/pull/148)) +* chore: Add format check to CI and format +([#146](https://github.com/aws/aws-dynamodb-encryption-java/pull/146)) +* chore: Add example for MRKs +([#145](https://github.com/aws/aws-dynamodb-encryption-java/pull/145)) +* chore: Add repo-sync actions +([#143](https://github.com/aws/aws-dynamodb-encryption-java/pull/143)) +* chore: Add issue template +([#142](https://github.com/aws/aws-dynamodb-encryption-java/pull/142)) + ## 2.0.1 -- 2021-02-12 ### Bugfix Fixes released jar files to ensure JDK 8 compatibility. diff --git a/README.md b/README.md index 75318a09..85e28872 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ You can download the [latest snapshot release][download] or pick it up from Mave com.amazonaws aws-dynamodb-encryption-java - 2.0.1 + 2.0.2 ``` diff --git a/examples/pom.xml b/examples/pom.xml index 4f8bc3c9..a3b86024 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -8,7 +8,7 @@ software.amazon.cryptools dynamodbencryptionclient-pom - 2.0.1 + 2.0.2 dynamodbencryptionclient-sdk1examples diff --git a/pom.xml b/pom.xml index 1ce12fac..9724267a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 software.amazon.cryptools dynamodbencryptionclient-pom - 2.0.1 + 2.0.2 pom aws-dynamodb-encryption-java :: POM From 722e25b2ce52d599267926a56a19cc7b1e5d180e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Corella?= <39066999+josecorella@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:56:56 -0700 Subject: [PATCH 014/105] fix: updating version in pom in order to build in test-release (#156) --- examples/pom.xml | 4 ++-- sdk1/pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index a3b86024..6a0aff9b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -13,7 +13,7 @@ dynamodbencryptionclient-sdk1examples jar - 2.0.1 + 2.0.2 aws-dynamodb-encryption-java :: SDK1 Examples Examples for AWS DynamoDB Encryption Client for AWS Java SDK v1 @@ -23,7 +23,7 @@ 3.0.0-M3 8 8 - 2.0.1 + 2.0.2 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index e0902831..ebb9a6e5 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.amazonaws aws-dynamodb-encryption-java - 2.0.1 + 2.0.2 jar aws-dynamodb-encryption-java :: SDK1 AWS DynamoDB Encryption Client for AWS Java SDK v1 From fa56e7fcdbe5f8d2c1c001a27fb338057ebbb372 Mon Sep 17 00:00:00 2001 From: Alex Chew Date: Thu, 14 Oct 2021 09:56:11 -0700 Subject: [PATCH 015/105] load library version via local ClassLoader (#158) --- .../services/dynamodbv2/datamodeling/internal/Utils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java index 06abd3dc..be0abe3e 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java @@ -57,7 +57,8 @@ public static V checkNotNull(final V ref, final String errMsg) { public static String loadVersion() { try { final Properties properties = new Properties(); - properties.load(ClassLoader.getSystemResourceAsStream("project.properties")); + final ClassLoader loader = Utils.class.getClassLoader(); + properties.load(loader.getResourceAsStream("project.properties")); return properties.getProperty("version"); } catch (final IOException ex) { return "unknown"; From 124b381c960b99beaf535f3bdcd5c518d450a2b7 Mon Sep 17 00:00:00 2001 From: Alex Chew Date: Thu, 14 Oct 2021 11:41:40 -0700 Subject: [PATCH 016/105] chore: upgrade BouncyCastle to 1.69 (#159) --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index ebb9a6e5..388c4d78 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -191,7 +191,7 @@ org.bouncycastle bcprov-ext-jdk15on - 1.68 + 1.69 test From c29c926f7bc17ff87d192f055dcf18a8f9dfefbe Mon Sep 17 00:00:00 2001 From: Alex Chew Date: Thu, 14 Oct 2021 11:53:57 -0700 Subject: [PATCH 017/105] chore: bump version to 2.0.3, update changelog (#160) --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- examples/pom.xml | 6 +++--- pom.xml | 2 +- sdk1/pom.xml | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad81a54..28c11c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # Changelog + +## 2.0.3 -- 2021-10-14 + +### Fixes +* load library version via local ClassLoader + ([#158](https://github.com/aws/aws-dynamodb-encryption-java/pull/158)) + +### Maintenance +* chore: upgrade BouncyCastle to 1.69 + ([#159](https://github.com/aws/aws-dynamodb-encryption-java/pull/159/)) + ## 2.0.2 -- 2021-08-31 ### Fixes * fix: Allow reading old plaintext records after adding new encrypted field to schema diff --git a/README.md b/README.md index 85e28872..f22a3a23 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ You can download the [latest snapshot release][download] or pick it up from Mave com.amazonaws aws-dynamodb-encryption-java - 2.0.2 + 2.0.3 ``` diff --git a/examples/pom.xml b/examples/pom.xml index 6a0aff9b..6091f9a8 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -8,12 +8,12 @@ software.amazon.cryptools dynamodbencryptionclient-pom - 2.0.2 + 2.0.3 dynamodbencryptionclient-sdk1examples jar - 2.0.2 + 2.0.3 aws-dynamodb-encryption-java :: SDK1 Examples Examples for AWS DynamoDB Encryption Client for AWS Java SDK v1 @@ -23,7 +23,7 @@ 3.0.0-M3 8 8 - 2.0.2 + 2.0.3 diff --git a/pom.xml b/pom.xml index 9724267a..7e31847e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 software.amazon.cryptools dynamodbencryptionclient-pom - 2.0.2 + 2.0.3 pom aws-dynamodb-encryption-java :: POM diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 388c4d78..2788da9c 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.amazonaws aws-dynamodb-encryption-java - 2.0.2 + 2.0.3 jar aws-dynamodb-encryption-java :: SDK1 AWS DynamoDB Encryption Client for AWS Java SDK v1 From 66c679bd705a9dd64b6c05fe2bf71ba5a27e84c1 Mon Sep 17 00:00:00 2001 From: Ben Farley <47006790+farleyb-amazon@users.noreply.github.com> Date: Tue, 9 Nov 2021 16:15:10 -0700 Subject: [PATCH 018/105] chore: Add CODEOWNERS file (#163) --- .github/CODEOWNERS | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..80689173 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# Each line is a file pattern followed by one or more owners. +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default code owner for everything is our aws-crypto-tools group +* @aws/aws-crypto-tools From ccf3e6b86d4c5ffca126915956b4f1bb15442451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Corella?= <39066999+josecorella@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:29:12 -0800 Subject: [PATCH 019/105] chore: enable dependabot in our active branches and update gitignore (#166) --- .github/dependabot.yml | 15 +++++++++++++++ .gitignore | 3 +++ 2 files changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..e2b44e80 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + target-branch: "master-1.x" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.gitignore b/.gitignore index c2525ee2..980da906 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ cleanup.sh release.sh target +.settings/ +.project +.classpath .idea/ *.iml .DS_Store From 51eaa8dab0b4fe8f9dcee4b5b06fcf0af5e2c9b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Dec 2021 11:54:42 -0800 Subject: [PATCH 020/105] Bump maven-checkstyle-plugin from 3.1.0 to 3.1.2 (#169) Bumps [maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/apache/maven-checkstyle-plugin/releases) - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.1.0...maven-checkstyle-plugin-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 2788da9c..af6bf081 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -124,7 +124,7 @@ UTF-8 1.0.392 8.29 - 3.1.0 + 3.1.2 0.8.3 3.0.1 3.0.0 From 61c2083dcba58bbab922d3f77ce9b7877368b125 Mon Sep 17 00:00:00 2001 From: Tony Knapp <5892063+texastony@users.noreply.github.com> Date: Wed, 15 Dec 2021 12:45:49 -0800 Subject: [PATCH 021/105] fix(log): remove unused logging (#177) --- .../dynamodbv2/datamodeling/AttributeEncryptor.java | 3 --- sdk1/src/test/java/log4j.properties | 10 ---------- 2 files changed, 13 deletions(-) delete mode 100644 sdk1/src/test/java/log4j.properties diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java index 12d8a34f..49bdae97 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java @@ -32,8 +32,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Encrypts all non-key fields prior to storing them in DynamoDB. This must be used with {@link @@ -46,7 +44,6 @@ * @author Greg Rubin */ public class AttributeEncryptor implements AttributeTransformer { - private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class); private final DynamoDBEncryptor encryptor; private final Map, ModelClassMetadata> metadataCache = new ConcurrentHashMap<>(); diff --git a/sdk1/src/test/java/log4j.properties b/sdk1/src/test/java/log4j.properties deleted file mode 100644 index 1bc93d35..00000000 --- a/sdk1/src/test/java/log4j.properties +++ /dev/null @@ -1,10 +0,0 @@ -log4j.rootLogger=INFO, A1 -log4j.appender.A1=org.apache.log4j.ConsoleAppender -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -# Print the date in ISO 8601 format -log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n -# Adjust to see more / less logging -#log4j.logger.httpclient.wire=TRACE -log4j.logger.com.amazonaws=DEBUG -# HttpClient 4 Wire Logging -# log4j.logger.org.apache.http.wire=DEBUG \ No newline at end of file From a4bd03d8f5c3a13a7df9ecd3cd056d0baa3e8a4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Dec 2021 17:06:08 -0800 Subject: [PATCH 022/105] Bump jackson-core from 2.12.4 to 2.13.0 (#171) Bumps [jackson-core](https://github.com/FasterXML/jackson-core) from 2.12.4 to 2.13.0. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.12.4...jackson-core-2.13.0) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index af6bf081..7a09d637 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.12.4 + 2.13.0 test From df34f816d9f80e8a930cae132d751ab01d34f7b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:05:32 -0800 Subject: [PATCH 023/105] Bump maven-javadoc-plugin from 3.0.1 to 3.3.1 (#183) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.0.1 to 3.3.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.0.1...maven-javadoc-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 7a09d637..71e828f9 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -299,7 +299,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.3.1 *.internal:*.transform 128m From 2ec467d81e763c11ce9a686b9dfef575ae445c3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:21:51 -0800 Subject: [PATCH 024/105] Bump checkstyle from 8.29 to 9.2 (#184) Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 8.29 to 9.2. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.29...checkstyle-9.2) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 71e828f9..01066b8d 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -123,7 +123,7 @@ UTF-8 1.0.392 - 8.29 + 9.2 3.1.2 0.8.3 3.0.1 From 4c5efe1ee400d8051883e5db0f704c935a32963f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 13:10:43 -0800 Subject: [PATCH 025/105] Bump maven-jxr-plugin from 3.0.0 to 3.1.1 (#186) Bumps [maven-jxr-plugin](https://github.com/apache/maven-jxr) from 3.0.0 to 3.1.1. - [Release notes](https://github.com/apache/maven-jxr/releases) - [Commits](https://github.com/apache/maven-jxr/compare/jxr-3.0.0...jxr-3.1.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jxr-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 01066b8d..0fe5a1b1 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -127,7 +127,7 @@ 3.1.2 0.8.3 3.0.1 - 3.0.0 + 3.1.1 3.0.0-M3 3.0.0-M3 3.1.1 From 2908f5a47089b3f70e932ae8c488d6310a7f7278 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 14:08:18 -0800 Subject: [PATCH 026/105] Bump maven-dependency-plugin from 3.1.1 to 3.2.0 (#181) Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.1.1...maven-dependency-plugin-3.2.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 6091f9a8..2741e04d 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -99,7 +99,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.2.0 copy diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 0fe5a1b1..9b3c5d6e 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -310,7 +310,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.2.0 copy From 9ce5f49e706c299f60054e46b9118dbf9ac18fc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 14:15:45 -0800 Subject: [PATCH 027/105] Bump aws-java-sdk-bom from 1.12.51 to 1.12.129 (#178) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.51 to 1.12.129. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.51...1.12.129) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 9b3c5d6e..3c07f239 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.51 + 1.12.129 pom import From 320320ca9690ce31b24fcb28d0826757708fefea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 15:49:03 -0800 Subject: [PATCH 028/105] Bump jacoco-maven-plugin from 0.8.3 to 0.8.7 (#192) Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.3 to 0.8.7. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.3...v0.8.7) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 3c07f239..768a7978 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -125,7 +125,7 @@ 1.0.392 9.2 3.1.2 - 0.8.3 + 0.8.7 3.0.1 3.1.1 3.0.0-M3 From f90b7fb336893c83876078718b9620b7249b7c65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 16:35:06 -0800 Subject: [PATCH 029/105] Bump maven-compiler-plugin from 3.8.0 to 3.8.1 (#193) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.0 to 3.8.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.0...maven-compiler-plugin-3.8.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 2741e04d..38fda12d 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -93,7 +93,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 768a7978..06b8957c 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -283,7 +283,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 1.8 1.8 From fd5e250625cf9aed7693d562c9f7a2af1286a076 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 17:16:57 -0800 Subject: [PATCH 030/105] Bump maven-gpg-plugin from 1.6 to 3.0.1 (#175) Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.0.1. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.0.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 06b8957c..fcad07ac 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -37,7 +37,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts @@ -73,7 +73,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts From 473ca957fb2266c4739c95ef2fa087320b434403 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 17:30:39 -0800 Subject: [PATCH 031/105] Bump maven-surefire-plugin from 3.0.0-M3 to 3.0.0-M5 (#176) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M3 to 3.0.0-M5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M3...surefire-3.0.0-M5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 38fda12d..f555b9d1 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ 1.0.392 3.0.0-M3 - 3.0.0-M3 + 3.0.0-M5 8 8 2.0.3 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index fcad07ac..22f9c376 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -129,7 +129,7 @@ 3.0.1 3.1.1 3.0.0-M3 - 3.0.0-M3 + 3.0.0-M5 3.1.1 From 87bd5a4524c357484ae707519676b3b370eccd0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:03:51 -0800 Subject: [PATCH 032/105] build(deps): bump testng from 6.10 to 7.4.0 (#197) Bumps [testng](https://github.com/cbeust/testng) from 6.10 to 7.4.0. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/6.10...7.4.0) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index f555b9d1..45e1809c 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -36,7 +36,7 @@ org.testng testng - 6.10 + 7.4.0 test diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 22f9c376..84144222 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -170,7 +170,7 @@ org.testng testng - 6.10 + 7.4.0 test From 0ffd0154e8323f6a386765abee40901c0425a0ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:34:10 -0800 Subject: [PATCH 033/105] build(deps-dev): bump junit from 4.13.1 to 4.13.2 (#195) Bumps [junit](https://github.com/junit-team/junit4) from 4.13.1 to 4.13.2. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.13.1...r4.13.2) --- updated-dependencies: - dependency-name: junit:junit dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 84144222..7ceeb0c8 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -257,7 +257,7 @@ junit junit - 4.13.1 + 4.13.2 test From 0f5cc9b532bdb505f42b0090dcced9831c441e50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 11:39:24 -0800 Subject: [PATCH 034/105] build(deps-dev): bump quicktheories from 0.25 to 0.26 (#202) Bumps quicktheories from 0.25 to 0.26. --- updated-dependencies: - dependency-name: org.quicktheories:quicktheories dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 7ceeb0c8..90849c07 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -177,7 +177,7 @@ org.quicktheories quicktheories - 0.25 + 0.26 test From f5bfa7b7270f0515d8f3e991be4ae45997a1018e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:56:21 -0800 Subject: [PATCH 035/105] build(deps): bump maven-source-plugin from 3.0.1 to 3.2.1 (#194) Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.0.1 to 3.2.1. - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.0.1...maven-source-plugin-3.2.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 90849c07..caab458e 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -293,7 +293,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.2.1 From 50579ea068e1ce2a7cde129d445b225cb6c4735d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Dec 2021 09:42:39 -0800 Subject: [PATCH 036/105] build(deps-dev): bump jackson-annotations from 2.12.4 to 2.13.1 (#208) Bumps [jackson-annotations](https://github.com/FasterXML/jackson) from 2.12.4 to 2.13.1. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index caab458e..0ab3610e 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -236,7 +236,7 @@ com.fasterxml.jackson.core jackson-annotations - 2.12.4 + 2.13.1 test From 24a9ead83c7f63d499ad80beeb241a2bee798a82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Dec 2021 10:42:19 -0800 Subject: [PATCH 037/105] build(deps-dev): bump jackson-core from 2.13.0 to 2.13.1 (#206) Bumps [jackson-core](https://github.com/FasterXML/jackson-core) from 2.13.0 to 2.13.1. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.13.0...jackson-core-2.13.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 0ab3610e..cc559ad8 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.13.0 + 2.13.1 test From 964d73760399136dc3c1cbff691a42fc40700fdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Dec 2021 11:29:22 -0800 Subject: [PATCH 038/105] build(deps): bump fmt-maven-plugin from 2.10 to 2.13 (#207) Bumps [fmt-maven-plugin](https://github.com/coveooss/fmt-maven-plugin) from 2.10 to 2.13. - [Release notes](https://github.com/coveooss/fmt-maven-plugin/releases) - [Commits](https://github.com/coveooss/fmt-maven-plugin/compare/2.10.0...2.13.0) --- updated-dependencies: - dependency-name: com.coveo:fmt-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e31847e..a93830c9 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ com.coveo fmt-maven-plugin - 2.10 + 2.13 From d0330658b520c05db3f40a90b6a2550b27395207 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Dec 2021 08:44:45 -0800 Subject: [PATCH 039/105] build(deps): bump aws-java-sdk-bom from 1.12.129 to 1.12.130 (#212) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.129 to 1.12.130. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.129...1.12.130) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index cc559ad8..ee2df52d 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.129 + 1.12.130 pom import From 1b34f01d96748278c783b4e22482a7be74aa081c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Dec 2021 09:19:36 -0800 Subject: [PATCH 040/105] build(deps-dev): bump bcprov-ext-jdk15on from 1.69 to 1.70 (#213) Bumps [bcprov-ext-jdk15on](https://github.com/bcgit/bc-java) from 1.69 to 1.70. - [Release notes](https://github.com/bcgit/bc-java/releases) - [Changelog](https://github.com/bcgit/bc-java/blob/master/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcprov-ext-jdk15on dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index ee2df52d..fecaa73d 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -191,7 +191,7 @@ org.bouncycastle bcprov-ext-jdk15on - 1.69 + 1.70 test From 2dad7fffeb213a31745c1001eee66ae65d14947e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Dec 2021 11:54:39 -0700 Subject: [PATCH 041/105] build(deps): bump checkstyle from 9.2 to 9.2.1 (#217) Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 9.2 to 9.2.1. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-9.2...checkstyle-9.2.1) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index fecaa73d..9cbbdbd3 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -123,7 +123,7 @@ UTF-8 1.0.392 - 9.2 + 9.2.1 3.1.2 0.8.7 3.0.1 From 14dd202212e470e82919b45fd3924e3fb73fda96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Dec 2021 12:02:27 -0700 Subject: [PATCH 042/105] build(deps): bump aws-java-sdk-bom from 1.12.130 to 1.12.131 (#215) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.130 to 1.12.131. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.130...1.12.131) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 9cbbdbd3..d9cbc934 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.130 + 1.12.131 pom import From dffbfce732f0e993c66e0c86989b9923dfcdbf81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 16:16:25 -0800 Subject: [PATCH 043/105] build(deps): bump aws-java-sdk-bom from 1.12.131 to 1.12.150 (#264) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.131 to 1.12.150. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.131...1.12.150) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index d9cbc934..7f3374c4 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.131 + 1.12.150 pom import From 27afb1440d2b8e575043f78319bd997a0e91b331 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 16:50:43 -0800 Subject: [PATCH 044/105] build(deps): bump testng from 7.4.0 to 7.5 (#225) Bumps [testng](https://github.com/cbeust/testng) from 7.4.0 to 7.5. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.4.0...7.5) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 45e1809c..4366a135 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -36,7 +36,7 @@ org.testng testng - 7.4.0 + 7.5 test diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 7f3374c4..cca85a92 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -170,7 +170,7 @@ org.testng testng - 7.4.0 + 7.5 test From f630680815ce22cd774af7a7fcfcdd0d7eb82ed9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 09:44:10 -0800 Subject: [PATCH 045/105] build(deps): bump maven-compiler-plugin from 3.8.1 to 3.9.0 (#240) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.1 to 3.9.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.9.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 4366a135..93862b74 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -93,7 +93,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.9.0 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index cca85a92..b2730fa1 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -283,7 +283,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.9.0 1.8 1.8 From 904ef2f599dc30246a0127e9146e35e03e406149 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 10:49:42 -0800 Subject: [PATCH 046/105] build(deps): bump checkstyle from 9.2.1 to 9.3 (#268) Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 9.2.1 to 9.3. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-9.2.1...checkstyle-9.3) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index b2730fa1..43a4a5da 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -123,7 +123,7 @@ UTF-8 1.0.392 - 9.2.1 + 9.3 3.1.2 0.8.7 3.0.1 From 3037b5f6d0ea2b1977e1e361c5bc1d521edc2a01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 11:00:06 -0800 Subject: [PATCH 047/105] build(deps): bump aws-java-sdk-bom from 1.12.150 to 1.12.151 (#267) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.151. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.151) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 43a4a5da..e5038b16 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.150 + 1.12.151 pom import From c786e678d246ef58df53f22147f7b9821af6288b Mon Sep 17 00:00:00 2001 From: Kaiyao Ke <47203510+kaiyaok2@users.noreply.github.com> Date: Fri, 11 Feb 2022 01:19:57 +0800 Subject: [PATCH 048/105] fix: Account for indeterminate Map ordering in unit tests (#274) --- .../datamodeling/encryption/DynamoDBEncryptorTest.java | 6 ++++-- .../internal/AttributeValueMarshallerTest.java | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index 6ec4aefd..8ae6028d 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -49,6 +49,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import javax.crypto.KeyGenerator; @@ -198,7 +199,8 @@ public void ensureEncryptedAttributesUnmodified() throws GeneralSecurityExceptio Map encryptedAttributes = encryptor.encryptAllFieldsExcept( Collections.unmodifiableMap(attribs), context, "hashKey", "rangeKey", "version"); - String encryptedString = encryptedAttributes.toString(); + // Using TreeMap before casting to string to avoid nondeterministic key orders. + String encryptedString = new TreeMap<>(encryptedAttributes).toString(); encryptor.decryptAllFieldsExcept( Collections.unmodifiableMap(encryptedAttributes), context, @@ -206,7 +208,7 @@ public void ensureEncryptedAttributesUnmodified() throws GeneralSecurityExceptio "rangeKey", "version"); - assertEquals(encryptedString, encryptedAttributes.toString()); + assertEquals(encryptedString, new TreeMap<>(encryptedAttributes).toString()); } @Test(expectedExceptions = SignatureException.class) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java index 523a1958..5ff73f4f 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshallerTest.java @@ -266,9 +266,13 @@ public void testSimpleMapWithNull() { marshall(av); Assert.fail("Unexpected success"); } catch (final NullPointerException npe) { - Assert.assertEquals( - "Encountered null map value for key NullKeyValue while marshalling attribute value {M: {KeyValue={S: ValueValue,}, NullKeyValue=null},}", - npe.getMessage()); + // Map entries may permute under nondeterministic Java API + String npeMessage = npe.getMessage(); + String common = + "Encountered null map value for key NullKeyValue while marshalling attribute value "; + String case1 = common + "{M: {KeyValue={S: ValueValue,}, NullKeyValue=null},}"; + String case2 = common + "{M: {NullKeyValue=null, KeyValue={S: ValueValue,}},}"; + Assert.assertTrue(case1.equals(npeMessage) || case2.equals(npeMessage)); } } From a296d162618807b0d588686571dab7b300111af2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 12:33:10 -0700 Subject: [PATCH 049/105] build(deps): bump aws-java-sdk-bom from 1.12.151 to 1.12.282 (#548) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.151 to 1.12.282. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.151...1.12.282) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index e5038b16..471d2cf0 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.151 + 1.12.282 pom import From 9ba1063cec4fb7895cf858d1a7591ababc2a6bd4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 12:39:49 -0700 Subject: [PATCH 050/105] build(deps): bump actions/checkout from 2 to 3 (#312) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/repo-sync.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index b7605354..6b1c6be3 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -9,7 +9,7 @@ jobs: environment: repo-sync runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: repo-sync/github-sync@v2 name: Sync repo to branch with: From 48e33b03e88f048d2e0ec54295f988c470da2bb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:42:16 -0700 Subject: [PATCH 051/105] build(deps): bump maven-javadoc-plugin from 3.3.1 to 3.4.1 (#544) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.1 to 3.4.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.1...maven-javadoc-plugin-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 471d2cf0..c8cc6791 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -299,7 +299,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.1 + 3.4.1 *.internal:*.transform 128m From 79534832050e76a583cc77478a08c848972bc3e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:44:41 -0700 Subject: [PATCH 052/105] build(deps): bump maven-deploy-plugin from 2.8.2 to 3.0.0 (#551) Bumps [maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 2.8.2 to 3.0.0. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-2.8.2...maven-deploy-plugin-3.0.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a93830c9..75529925 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.0.0 true diff --git a/sdk1/pom.xml b/sdk1/pom.xml index c8cc6791..2ad08383 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -428,7 +428,7 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.0.0 false From 74a16041dd2b37022ee31180567008d1c9e1cff2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:55:06 -0700 Subject: [PATCH 053/105] build(deps): bump maven-compiler-plugin from 3.9.0 to 3.10.1 (#326) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.9.0 to 3.10.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.9.0...maven-compiler-plugin-3.10.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 93862b74..c8b54fa5 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -93,7 +93,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.9.0 + 3.10.1 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 2ad08383..28f636ed 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -283,7 +283,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.9.0 + 3.10.1 1.8 1.8 From 7837d491b8a295126d28ee0d0fc06f96f4fad419 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:01:51 -0700 Subject: [PATCH 054/105] build(deps-dev): bump jackson-annotations from 2.13.1 to 2.13.3 (#554) Bumps [jackson-annotations](https://github.com/FasterXML/jackson) from 2.13.1 to 2.13.3. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 28f636ed..f6dab37b 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -236,7 +236,7 @@ com.fasterxml.jackson.core jackson-annotations - 2.13.1 + 2.13.3 test From fb1349519c005ccd9c71c10bb97b190ebed120d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:08:54 -0700 Subject: [PATCH 055/105] build(deps): bump jacoco-maven-plugin from 0.8.7 to 0.8.8 (#556) Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.7 to 0.8.8. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.7...v0.8.8) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index f6dab37b..99df86ad 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -125,7 +125,7 @@ 1.0.392 9.3 3.1.2 - 0.8.7 + 0.8.8 3.0.1 3.1.1 3.0.0-M3 From 882151e5b5e00cc34339f9d6c1c1a18ba072e7a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 10:41:33 -0400 Subject: [PATCH 056/105] build(deps): bump maven-surefire-plugin from 3.0.0-M5 to 3.0.0-M7 (#562) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M5 to 3.0.0-M7. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M5...surefire-3.0.0-M7) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrew Jewell <107044381+ajewellamz@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index c8b54fa5..b1d87ee4 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ 1.0.392 3.0.0-M3 - 3.0.0-M5 + 3.0.0-M7 8 8 2.0.3 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 99df86ad..3ed72f40 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -129,7 +129,7 @@ 3.0.1 3.1.1 3.0.0-M3 - 3.0.0-M5 + 3.0.0-M7 3.1.1 From e3d115a0a8fb34c4c9188b83384bedd79b00cb23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 10:58:17 -0400 Subject: [PATCH 057/105] build(deps): bump maven-failsafe-plugin from 3.0.0-M3 to 3.0.0-M7 (#446) Bumps [maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M3 to 3.0.0-M7. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M3...surefire-3.0.0-M7) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index b1d87ee4..90ee707f 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -19,7 +19,7 @@ 1.0.392 - 3.0.0-M3 + 3.0.0-M7 3.0.0-M7 8 8 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 3ed72f40..8721fa0c 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -128,7 +128,7 @@ 0.8.8 3.0.1 3.1.1 - 3.0.0-M3 + 3.0.0-M7 3.0.0-M7 3.1.1 From 385dd52bcbb8cf0e0f2343798987e365ef52d697 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:07:04 -0400 Subject: [PATCH 058/105] build(deps): bump maven-dependency-plugin from 3.2.0 to 3.3.0 (#571) Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.2.0...maven-dependency-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrew Jewell <107044381+ajewellamz@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 90ee707f..5f844b80 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -99,7 +99,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.2.0 + 3.3.0 copy diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 8721fa0c..f77e2a77 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -310,7 +310,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.2.0 + 3.3.0 copy From 6f1c597746440e8850e30e95957fda9c05b41641 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:15:29 -0400 Subject: [PATCH 059/105] build(deps): bump nexus-staging-maven-plugin from 1.6.8 to 1.6.13 (#574) Bumps nexus-staging-maven-plugin from 1.6.8 to 1.6.13. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index f77e2a77..2317f32f 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -94,7 +94,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true sonatype-nexus-staging From 8e7cf0b31c19261039629b0828cd004a76599fd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:24:39 -0400 Subject: [PATCH 060/105] build(deps): bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 (#576) Bumps [maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.1.2 to 3.2.0. - [Release notes](https://github.com/apache/maven-checkstyle-plugin/releases) - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.1.2...maven-checkstyle-plugin-3.2.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrew Jewell <107044381+ajewellamz@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 2317f32f..2022b4bc 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -124,7 +124,7 @@ UTF-8 1.0.392 9.3 - 3.1.2 + 3.2.0 0.8.8 3.0.1 3.1.1 From dfcefb80fd6cbe3660826d91f09df2d8ad1acda2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:35:24 -0400 Subject: [PATCH 061/105] build(deps-dev): bump jackson-annotations from 2.13.3 to 2.13.4 (#577) Bumps [jackson-annotations](https://github.com/FasterXML/jackson) from 2.13.3 to 2.13.4. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 2022b4bc..f7c8fdce 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -236,7 +236,7 @@ com.fasterxml.jackson.core jackson-annotations - 2.13.3 + 2.13.4 test From 3c66f45ed78722f22e8228c1cdd33e4999ca3bbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:48:56 -0400 Subject: [PATCH 062/105] build(deps): bump aws-java-sdk-bom from 1.12.282 to 1.12.306 (#578) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.282 to 1.12.306. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.282...1.12.306) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index f7c8fdce..5f6f9fd0 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.282 + 1.12.306 pom import From 5adf26874cfb93366a946a8538f947a64976ed1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 12:23:58 -0400 Subject: [PATCH 063/105] build(deps-dev): bump jackson-core from 2.13.1 to 2.13.4 (#579) Bumps [jackson-core](https://github.com/FasterXML/jackson-core) from 2.13.1 to 2.13.4. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.13.1...jackson-core-2.13.4) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 5f6f9fd0..e3d9dca5 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.13.1 + 2.13.4 test From 1a897e781ce84b443f450f72259bed2a1af3719f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Sep 2022 09:21:39 -0400 Subject: [PATCH 064/105] build(deps): bump maven-jxr-plugin from 3.1.1 to 3.3.0 (#581) Bumps [maven-jxr-plugin](https://github.com/apache/maven-jxr) from 3.1.1 to 3.3.0. - [Release notes](https://github.com/apache/maven-jxr/releases) - [Commits](https://github.com/apache/maven-jxr/compare/jxr-3.1.1...jxr-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jxr-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index e3d9dca5..b88bb3b7 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -127,7 +127,7 @@ 3.2.0 0.8.8 3.0.1 - 3.1.1 + 3.3.0 3.0.0-M7 3.0.0-M7 3.1.1 From 22685eb78df983c62f725da42c333e897579a50a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Sep 2022 09:30:21 -0400 Subject: [PATCH 065/105] build(deps): bump aws-java-sdk-bom from 1.12.306 to 1.12.308 (#583) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.306 to 1.12.308. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.306...1.12.308) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index b88bb3b7..db144e37 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.306 + 1.12.308 pom import From 862d2bc12a8a7fb35045b80e52ed1b1ab7d6cc35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Sep 2022 07:30:04 -0400 Subject: [PATCH 066/105] build(deps): bump aws-java-sdk-bom from 1.12.308 to 1.12.309 (#585) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.308 to 1.12.309. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.308...1.12.309) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index db144e37..a37a48c4 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.308 + 1.12.309 pom import From 0f150d00ef66584397f5d7d2f364987d2208c5a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 07:21:33 -0400 Subject: [PATCH 067/105] build(deps): bump aws-java-sdk-bom from 1.12.309 to 1.12.318 (#604) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.309 to 1.12.318. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.309...1.12.318) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index a37a48c4..6fc93fa1 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.309 + 1.12.318 pom import From 4d5d8b3ac68c4fe584f21c3e567c4feb482a7393 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 11:07:09 -0400 Subject: [PATCH 068/105] build(deps): bump aws-java-sdk-bom from 1.12.318 to 1.12.319 (#607) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.318 to 1.12.319. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.318...1.12.319) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 6fc93fa1..c7becb54 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.318 + 1.12.319 pom import From 18b020acbca57e2ce755a8a4f2df2a64e9d5186c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 10:01:04 +0000 Subject: [PATCH 069/105] build(deps): bump aws-java-sdk-bom from 1.12.319 to 1.12.321 Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.319 to 1.12.321. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.319...1.12.321) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index c7becb54..a198cf2e 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.319 + 1.12.321 pom import From 70fd8835c94bbef2ca7fca1ec492ab6b1313ab60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Oct 2022 11:25:19 -0700 Subject: [PATCH 070/105] build(deps): bump aws-java-sdk-bom from 1.12.321 to 1.12.323 (#615) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.321 to 1.12.323. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.321...1.12.323) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index a198cf2e..95fa70d6 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.321 + 1.12.323 pom import From 526dc069aef733bd01cde32166858495a64767a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:57:49 -0400 Subject: [PATCH 071/105] build(deps): bump aws-java-sdk-bom from 1.12.323 to 1.12.333 (#637) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.323 to 1.12.333. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.323...1.12.333) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 95fa70d6..6cc975af 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.323 + 1.12.333 pom import From 6e36634dea49220c7db62ed4bf5472e75fba25db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:10:35 -0400 Subject: [PATCH 072/105] build(deps): bump aws-java-sdk-bom from 1.12.333 to 1.12.335 (#641) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.333 to 1.12.335. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.333...1.12.335) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 6cc975af..041a34b7 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.333 + 1.12.335 pom import From 4e3d5eed7d1de09a7306fc9704371e43dd7a4dde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:53:41 -0500 Subject: [PATCH 073/105] build(deps-dev): bump jackson-annotations from 2.13.4 to 2.14.0 (#643) Bumps [jackson-annotations](https://github.com/FasterXML/jackson) from 2.13.4 to 2.14.0. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 041a34b7..885d57a7 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -236,7 +236,7 @@ com.fasterxml.jackson.core jackson-annotations - 2.13.4 + 2.14.0 test From 8a9b205e57aceecc3139f3a8cf88e5da938d5db5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 09:03:11 -0500 Subject: [PATCH 074/105] build(deps-dev): bump jackson-core from 2.13.4 to 2.14.0 (#645) Bumps [jackson-core](https://github.com/FasterXML/jackson-core) from 2.13.4 to 2.14.0. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.13.4...jackson-core-2.14.0) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 885d57a7..d798419e 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.13.4 + 2.14.0 test From ceb9fcd0acce1e9d39ad64424be81227497d422b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 09:18:58 -0500 Subject: [PATCH 075/105] build(deps): bump aws-java-sdk-bom from 1.12.335 to 1.12.337 (#646) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.335 to 1.12.337. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.335...1.12.337) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index d798419e..a844ee6f 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.335 + 1.12.337 pom import From 609fe13d0479b926a741f35b64028a6f2808a72b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 14:33:20 -0800 Subject: [PATCH 076/105] build(deps-dev): bump jackson-annotations from 2.14.0 to 2.14.1 (#668) Bumps [jackson-annotations](https://github.com/FasterXML/jackson) from 2.14.0 to 2.14.1. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index a844ee6f..70344be8 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -236,7 +236,7 @@ com.fasterxml.jackson.core jackson-annotations - 2.14.0 + 2.14.1 test From 6792d9f253a9854faae9b94d5aa62d2e90370290 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 11:07:19 -0500 Subject: [PATCH 077/105] build(deps-dev): bump jackson-core from 2.14.0 to 2.14.1 (#670) Bumps [jackson-core](https://github.com/FasterXML/jackson-core) from 2.14.0 to 2.14.1. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.14.0...jackson-core-2.14.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 70344be8..740e1437 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.14.0 + 2.14.1 test From c2896f5bfb3c9ce33a0a60a3e584e1607f3a48be Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 5 Jan 2023 11:55:10 -0800 Subject: [PATCH 078/105] chore: Run CodeBuild CI from Github Actions (#737) --- .github/workflows/run-codebuild-ci.yml | 55 ++++++++++++++++++++++++++ buildspec.yml | 30 -------------- 2 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/run-codebuild-ci.yml delete mode 100644 buildspec.yml diff --git a/.github/workflows/run-codebuild-ci.yml b/.github/workflows/run-codebuild-ci.yml new file mode 100644 index 00000000..7807fbc9 --- /dev/null +++ b/.github/workflows/run-codebuild-ci.yml @@ -0,0 +1,55 @@ +name: AWS CodeBuild CI +on: + pull_request: + push: + schedule: + - cron: "0 0 * * *" # Daily at 00:00 UTC (4 PM PDT) + +permissions: + id-token: write + contents: read + +jobs: + staticAnalysis: + name: Static Analysis + runs-on: ubuntu-latest + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} + aws-region: us-west-2 + role-duration-seconds: 3600 + - name: Run Static Analysis + uses: aws-actions/aws-codebuild-run-build@v1 + timeout-minutes: 60 + with: + project-name: java-ddb-ec + buildspec-override: codebuild/static-analysis.yml + image-override: aws/codebuild/amazonlinux2-x86_64-standard:3.0 + build: + name: Build + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + platform: + - distribution: openjdk + image: "aws/codebuild/standard:3.0" + - distribution: corretto + image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0" # Corretto only runs on AL2 + version: [ 8, 11 ] + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} + aws-region: us-west-2 + role-duration-seconds: 3600 + - name: Build + uses: aws-actions/aws-codebuild-run-build@v1 + timeout-minutes: 60 + with: + project-name: java-ddb-ec + buildspec-override: codebuild/${{ matrix.platform.distribution }}${{ matrix.version }}.yml + image-override: ${{ matrix.platform.image }} diff --git a/buildspec.yml b/buildspec.yml deleted file mode 100644 index 1fc7d652..00000000 --- a/buildspec.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: 0.2 - -batch: - fast-fail: false - build-list: - - identifier: openjdk8 - buildspec: codebuild/openjdk8.yml - env: - env: - image: aws/codebuild/standard:3.0 - - identifier: openjdk11 - buildspec: codebuild/openjdk11.yml - env: - env: - image: aws/codebuild/standard:3.0 - - identifier: corretto8 - buildspec: codebuild/corretto8.yml - env: - env: - image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 - - identifier: corretto11 - buildspec: codebuild/corretto11.yml - env: - env: - image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 - - identifier: static_analysis - buildspec: codebuild/static-analysis.yml - env: - env: - image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 From 369fcbde3ed84c68dcebbefaf8ed5665a5c766e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Jan 2023 08:42:24 -0500 Subject: [PATCH 079/105] build(deps): bump maven-dependency-plugin from 3.3.0 to 3.4.0 (#685) Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.3.0...maven-dependency-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 5f844b80..71a42ff3 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -99,7 +99,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.3.0 + 3.4.0 copy diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 740e1437..160f4fa5 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -310,7 +310,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.3.0 + 3.4.0 copy From 8483e1eadeab5a6bf0ed63242eccec25ff6791a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Jan 2023 11:46:21 -0500 Subject: [PATCH 080/105] build(deps): bump aws-java-sdk-bom from 1.12.337 to 1.12.379 (#738) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.337 to 1.12.379. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.337...1.12.379) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 160f4fa5..889d8ad2 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.337 + 1.12.379 pom import From 5d9438b7cf1d605929326287529ead61434377c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:10:48 -0500 Subject: [PATCH 081/105] build(deps): bump aws-java-sdk-bom from 1.12.379 to 1.12.381 (#742) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.379 to 1.12.381. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.379...1.12.381) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 889d8ad2..c3939ef0 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.379 + 1.12.381 pom import From 9640e8972b4a9b495bd9fbebae1790a4b9da09c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:16:07 -0800 Subject: [PATCH 082/105] build(deps): bump aws-java-sdk-bom from 1.12.381 to 1.12.390 (#763) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.381 to 1.12.390. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.381...1.12.390) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index c3939ef0..d18071cc 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.381 + 1.12.390 pom import From 16afcf4f2d0e729ea37f4788257c190497499481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Jan 2023 09:04:43 -0800 Subject: [PATCH 083/105] build(deps): bump maven-checkstyle-plugin from 3.2.0 to 3.2.1 (#747) Bumps [maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/apache/maven-checkstyle-plugin/releases) - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.2.0...maven-checkstyle-plugin-3.2.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index d18071cc..a96c6abb 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -124,7 +124,7 @@ UTF-8 1.0.392 9.3 - 3.2.0 + 3.2.1 0.8.8 3.0.1 3.3.0 From 43a50d785c1420f403e04f1b14e9533a6005cd61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 13:44:53 -0800 Subject: [PATCH 084/105] build(deps): bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#746) Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.4.0 to 3.5.0. - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.4.0...maven-dependency-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 71a42ff3..9b7d57de 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -99,7 +99,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.4.0 + 3.5.0 copy diff --git a/sdk1/pom.xml b/sdk1/pom.xml index a96c6abb..6c66869b 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -310,7 +310,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.4.0 + 3.5.0 copy From 2cda7bb66dd9e21d473689d3e97852ad9db640be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:55:53 -0800 Subject: [PATCH 085/105] build(deps-dev): bump jackson-core from 2.14.1 to 2.14.2 (#772) Bumps [jackson-core](https://github.com/FasterXML/jackson-core) from 2.14.1 to 2.14.2. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.14.1...jackson-core-2.14.2) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 6c66869b..62237589 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.14.1 + 2.14.2 test From 6536e7406c9746079a809fb8c607d622e17e13f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 09:56:07 -0500 Subject: [PATCH 086/105] build(deps): bump aws-java-sdk-bom from 1.12.390 to 1.12.411 (#810) Bumps [aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.390 to 1.12.411. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.390...1.12.411) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 62237589..130533ce 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.390 + 1.12.411 pom import From 1f491c520cb0759e02ec704c60f80b5bb09b10b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 10:38:33 -0700 Subject: [PATCH 087/105] build(deps-dev): bump testng from 7.5 to 7.5.1 in /examples (#882) Bumps [testng](https://github.com/cbeust/testng) from 7.5 to 7.5.1. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.5...7.5.1) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index 9b7d57de..18caa68a 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -36,7 +36,7 @@ org.testng testng - 7.5 + 7.5.1 test From 130e28f912f05909bc255fe4e9ac8b00c9385567 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 17:18:38 -0700 Subject: [PATCH 088/105] build(deps-dev): bump testng from 7.5 to 7.5.1 in /sdk1 (#883) Bumps [testng](https://github.com/cbeust/testng) from 7.5 to 7.5.1. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.5...7.5.1) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 130533ce..5fe88dff 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -170,7 +170,7 @@ org.testng testng - 7.5 + 7.5.1 test From ba75855a49ff4f09bfadb032e97e37298df588f1 Mon Sep 17 00:00:00 2001 From: Tony Knapp <5892063+texastony@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:48:14 -0700 Subject: [PATCH 089/105] fix(docs): Update AWS Doc URLs (#913) --- CHANGELOG.md | 2 +- README.md | 2 +- .../services/dynamodbv2/datamodeling/AttributeEncryptor.java | 2 +- .../dynamodbv2/datamodeling/encryption/DoNotEncrypt.java | 2 +- .../services/dynamodbv2/datamodeling/encryption/DoNotTouch.java | 2 +- .../dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java | 2 +- .../dynamodbv2/datamodeling/encryption/DynamoDBSigner.java | 2 +- .../datamodeling/encryption/HandleUnknownAttributes.java | 2 +- .../dynamodbv2/datamodeling/encryption/TableAadOverride.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28c11c21..55f4ae6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ CachingMostRecentProvider replaces MostRecentProvider and provides a cache entry the key with the key provider. MostRecentProvider is now deprecated, and is removed in 2.0.0. -See https://docs.aws.amazon.com/dynamodb-encryption-client/latest/devguide/most-recent-provider.html#mrp-versions for more details. +See https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/most-recent-provider.html#mrp-versions for more details. 1.15.0 also fixes interoperability issues between the Python and Java implementations of DynamoDB Encryption Client. diff --git a/README.md b/README.md index f22a3a23..66e4b583 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ automatically. Whenever you change your data model, that is, when you add or remove attributes from your table items, you need to take additional steps to safely migrate the client-side encryption configuration. -For guidance on this process, please see the developer guide on [Changing Your Data Model](https://docs.aws.amazon.com/dynamodb-encryption-client/latest/devguide/data-model.html). +For guidance on this process, please see the developer guide on [Changing Your Data Model](https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/data-model.html). ### Downloads diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java index 49bdae97..8b1e9cc5 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java @@ -38,7 +38,7 @@ * SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}. * *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Greg Rubin diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java index 45f49096..559964ea 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java @@ -24,7 +24,7 @@ * Prevents the associated item (class or attribute) from being encrypted. * *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Greg Rubin diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java index 6988ed1f..c57011a3 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java @@ -24,7 +24,7 @@ * Prevents the associated item from being encrypted or signed. * *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Greg Rubin diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 3a40c1d4..8b8856ea 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -50,7 +50,7 @@ * attributes. * *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Greg Rubin diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java index 3a4aa14d..a4c23a39 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java @@ -42,7 +42,7 @@ /** * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Greg Rubin diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java index 282faf10..123d4a47 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java @@ -31,7 +31,7 @@ * encryption behavior, the unknown attributes will be signed and decrypted. * *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Dan Cavallaro diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java index ca5ad067..b7818815 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java @@ -25,7 +25,7 @@ * to be copied or moved between them without needing to be reencrypted. * *

For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model * * @author Greg Rubin From 17cb3a723c332dba78685bb18ef9d42d3c5b82b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 11:35:25 -0700 Subject: [PATCH 090/105] build(deps-dev): bump com.fasterxml.jackson.core:jackson-annotations (#951) Bumps [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson) from 2.14.1 to 2.15.2. - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 5fe88dff..87911f4f 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -236,7 +236,7 @@ com.fasterxml.jackson.core jackson-annotations - 2.14.1 + 2.15.2 test From 62ab3ff963343a9f496975915e6c1fb3c2170ec0 Mon Sep 17 00:00:00 2001 From: lavaleri <49660121+lavaleri@users.noreply.github.com> Date: Tue, 15 Aug 2023 13:16:49 -0700 Subject: [PATCH 091/105] chore: Add DB-ESDK 3.x section to README (#963) --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 66e4b583..324166e5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Client-side Encryption for Amazon DynamoDB +📣 Note: Version 3.x of this library is available, +and has been renamed to the AWS Database Encryption SDK. +See the [AWS Database Encryption SDK 3.x](#aws-database-encryption-sdk-3.x) section for more information. + The **[Amazon DynamoDB][ddb] Client-side Encryption in Java** supports encryption and signing of your data when stored in Amazon DynamoDB. A typical use of this library is when you are using [DynamoDBMapper][ddbmapper], where transparent protection of all objects serialized through the mapper can be enabled via configuring an [AttributeEncryptor][attrencryptor]. @@ -13,6 +17,15 @@ For more advanced use cases where tighter control over the encryption and signin See [Support Policy](./SUPPORT_POLICY.rst) for for details on the current support status of all major versions of this library. +## AWS Database Encryption SDK 3.x + +The 3.x version of this library is generally available, and has been [renamed to the AWS Database Encryption SDK](https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/DDBEC-rename.html). +It is a major rewrite of the DynamoDB Encryption Client for Java +and includes many updates, such as a new structured data format, improved multitenancy support, seamless schema changes, and searchable encryption support. + +For more information see the [AWS Database Encryption SDK Developer Guide](https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/what-is-database-encryption-sdk.html). +or check the project repository at [https://github.com/aws/aws-database-encryption-sdk-dynamodb-java/](https://github.com/aws/aws-database-encryption-sdk-dynamodb-java/). + ## Getting Started ### Required Prerequisites From d4b17a27960fb04d3ee5eb265510e1cb90ba7ac5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 14:18:36 -0700 Subject: [PATCH 092/105] build(deps): bump aws-actions/configure-aws-credentials from 1 to 2 (#823) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 1 to 2. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/v1...v2) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: lavaleri <49660121+lavaleri@users.noreply.github.com> --- .github/workflows/run-codebuild-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-codebuild-ci.yml b/.github/workflows/run-codebuild-ci.yml index 7807fbc9..78dce3d3 100644 --- a/.github/workflows/run-codebuild-ci.yml +++ b/.github/workflows/run-codebuild-ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} aws-region: us-west-2 @@ -41,7 +41,7 @@ jobs: version: [ 8, 11 ] steps: - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} aws-region: us-west-2 From 0db684d15400735d4e78bb7040d1d17be2742a4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 14:24:10 -0700 Subject: [PATCH 093/105] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin (#949) Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.0.1 to 3.1.0. - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.0.1...maven-gpg-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: lavaleri <49660121+lavaleri@users.noreply.github.com> --- sdk1/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 87911f4f..b27f976f 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -37,7 +37,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.1.0 sign-artifacts @@ -73,7 +73,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.1.0 sign-artifacts From 2a0185eb558e29895b96d09b1f134ec47dd0d6ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 12:53:43 -0700 Subject: [PATCH 094/105] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.8 to 0.8.10 (#968) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.8 to 0.8.10. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.8...v0.8.10) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index b27f976f..c26142a3 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -125,7 +125,7 @@ 1.0.392 9.3 3.2.1 - 0.8.8 + 0.8.10 3.0.1 3.3.0 3.0.0-M7 From b05d7f56129b0618251546771dcba10287b01998 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 16:37:26 -0700 Subject: [PATCH 095/105] build(deps-dev): bump com.fasterxml.jackson.core:jackson-core (#965) Bumps [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) from 2.14.2 to 2.15.2. - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.14.2...jackson-core-2.15.2) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index c26142a3..c0d63c76 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -243,7 +243,7 @@ com.fasterxml.jackson.core jackson-core - 2.14.2 + 2.15.2 test From 159b199c312310541b371f6e5d6c597114b2ecfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 10:28:18 +0000 Subject: [PATCH 096/105] build(deps): bump aws-actions/configure-aws-credentials from 2 to 3 Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 2 to 3. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/v2...v3) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/run-codebuild-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-codebuild-ci.yml b/.github/workflows/run-codebuild-ci.yml index 78dce3d3..b2f162ea 100644 --- a/.github/workflows/run-codebuild-ci.yml +++ b/.github/workflows/run-codebuild-ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} aws-region: us-west-2 @@ -41,7 +41,7 @@ jobs: version: [ 8, 11 ] steps: - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} aws-region: us-west-2 From fb61c31cd5db223be727a3aa6241a10eb371a689 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 22:32:09 +0000 Subject: [PATCH 097/105] build(deps): bump org.apache.maven.plugins:maven-compiler-plugin Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.1...maven-compiler-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 18caa68a..0d26f329 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -93,7 +93,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index c0d63c76..efce8788 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -283,7 +283,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 1.8 1.8 From 05b83b273bb59c4c81be7b424e39d586e99c990a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 22:37:34 +0000 Subject: [PATCH 098/105] build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.2.1 to 3.3.0. - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.2.1...maven-checkstyle-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index efce8788..c071d399 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -124,7 +124,7 @@ UTF-8 1.0.392 9.3 - 3.2.1 + 3.3.0 0.8.10 3.0.1 3.3.0 From f844b52e80a98dbe9ca84d29fb2f0a427c979ffb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 22:58:57 +0000 Subject: [PATCH 099/105] build(deps): bump org.apache.maven.plugins:maven-dependency-plugin Bumps [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.5.0 to 3.6.0. - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.5.0...maven-dependency-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 0d26f329..4388783f 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -99,7 +99,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.5.0 + 3.6.0 copy diff --git a/sdk1/pom.xml b/sdk1/pom.xml index c071d399..0a9818a2 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -310,7 +310,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.5.0 + 3.6.0 copy From 290eba246c813c6aec79ab1bcdbcc2256e590cdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 23:04:18 +0000 Subject: [PATCH 100/105] build(deps): bump org.apache.maven.plugins:maven-source-plugin Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.1 to 3.3.0. - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 0a9818a2..55e7e7b8 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -293,7 +293,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 From b0f95193efb7088ff8d3700ef2f2c52e08a7036d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:38:57 -0400 Subject: [PATCH 101/105] build(deps): bump maven-surefire-plugin from 3.0.0-M7 to 3.1.2 (#907) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M7 to 3.1.2. --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 4388783f..828783f2 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ 1.0.392 3.0.0-M7 - 3.0.0-M7 + 3.1.2 8 8 2.0.3 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 55e7e7b8..416e4f77 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -129,7 +129,7 @@ 3.0.1 3.3.0 3.0.0-M7 - 3.0.0-M7 + 3.1.2 3.1.1 From 4dbf527578cd30fced633c6aebd6cfe7556c64fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:51:32 -0400 Subject: [PATCH 102/105] build(deps): bump maven-failsafe-plugin from 3.0.0-M7 to 3.1.2 (#908) Bumps [maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M7 to 3.1.2. --- examples/pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 828783f2..6dd39852 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -19,7 +19,7 @@ 1.0.392 - 3.0.0-M7 + 3.1.2 3.1.2 8 8 diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 416e4f77..9ab35860 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -128,7 +128,7 @@ 0.8.10 3.0.1 3.3.0 - 3.0.0-M7 + 3.1.2 3.1.2 3.1.1 From 29977b1bae2d61d7221194846de46c73571ff1ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:04:18 -0400 Subject: [PATCH 103/105] build(deps): bump org.apache.maven.plugins:maven-deploy-plugin (#982) Bumps [org.apache.maven.plugins:maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.0.0 to 3.1.1. --- pom.xml | 2 +- sdk1/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 75529925..38dc9078 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0 + 3.1.1 true diff --git a/sdk1/pom.xml b/sdk1/pom.xml index 9ab35860..fbe7147e 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -428,7 +428,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0 + 3.1.1 false From 0dc48985a4875fcd6627715bfeb76dd871d505c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 11:23:14 -0400 Subject: [PATCH 104/105] build(deps): bump com.amazonaws:aws-java-sdk-bom (#984) Bumps [com.amazonaws:aws-java-sdk-bom](https://github.com/aws/aws-sdk-java) from 1.12.411 to 1.12.540. --- sdk1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/pom.xml b/sdk1/pom.xml index fbe7147e..f0d4d6ea 100644 --- a/sdk1/pom.xml +++ b/sdk1/pom.xml @@ -149,7 +149,7 @@ com.amazonaws aws-java-sdk-bom - 1.12.411 + 1.12.540 pom import From c7450c5e861d099a98a59825d0c7b45c6fd65116 Mon Sep 17 00:00:00 2001 From: seebees Date: Tue, 12 Dec 2023 10:54:05 -0800 Subject: [PATCH 105/105] feat: Update overrideEncryptionContextTableName (#1112) The names in `overrideEncryptionContextTableName` are relative to the code and not the customer using the code. This updates these names and docs to reflect the intuition customers have when dealing with two different table names. --------- Co-authored-by: texastony <5892063+texastony@users.noreply.github.com> --- ...ionContextOverridesWithDynamoDBMapper.java | 15 ++++++------ ...nContextOverridesWithDynamoDBMapperIT.java | 4 ++-- .../utils/EncryptionContextOperators.java | 23 ++++++++++++------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java b/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java index 07e4fcfa..c7a84302 100644 --- a/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java +++ b/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java @@ -46,7 +46,8 @@ * "partition_attribute" for Strings and a sort (range) key named "sort_attribute" for numbers. */ public class EncryptionContextOverridesWithDynamoDBMapper { - public static final String TABLE_NAME_TO_OVERRIDE = "ExampleTableForEncryptionContextOverrides"; + public static final String ORIGINAL_TABLE_NAME_TO_OVERRIDE = + "ExampleTableForEncryptionContextOverrides"; public static final String PARTITION_ATTRIBUTE = "partition_attribute"; public static final String SORT_ATTRIBUTE = "sort_attribute"; @@ -78,7 +79,7 @@ public static void main(String[] args) throws GeneralSecurityException { public static void encryptRecord( final String cmkArn, - final String newEncryptionContextTableName, + final String currentTableName, AmazonDynamoDB ddbClient, AWSKMS kmsClient) throws GeneralSecurityException { @@ -95,7 +96,7 @@ public static void encryptRecord( final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); Map tableNameEncryptionContextOverrides = new HashMap<>(); - tableNameEncryptionContextOverrides.put(TABLE_NAME_TO_OVERRIDE, newEncryptionContextTableName); + tableNameEncryptionContextOverrides.put(ORIGINAL_TABLE_NAME_TO_OVERRIDE, currentTableName); tableNameEncryptionContextOverrides.put( "AnotherExampleTableForEncryptionContextOverrides", "this table doesn't exist"); @@ -133,7 +134,7 @@ public static void encryptRecord( final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); final Map encryptedItem = - ddbClient.getItem(TABLE_NAME_TO_OVERRIDE, itemKey).getItem(); + ddbClient.getItem(ORIGINAL_TABLE_NAME_TO_OVERRIDE, itemKey).getItem(); System.out.println("Encrypted Record: " + encryptedItem); Map> encryptionFlags = new HashMap<>(); @@ -151,11 +152,11 @@ public static void encryptRecord( new EncryptionContext.Builder() .withHashKeyName(PARTITION_ATTRIBUTE) .withRangeKeyName(SORT_ATTRIBUTE) - .withTableName(newEncryptionContextTableName) + .withTableName(currentTableName) .build()); System.out.printf( "The example item was encrypted using the table name '%s' in the EncryptionContext%n", - newEncryptionContextTableName); + currentTableName); // The decrypted field matches the original field before encryption assert record @@ -163,7 +164,7 @@ public static void encryptRecord( .equals(decrypted_without_override_record.get(STRING_FIELD_NAME).getS()); } - @DynamoDBTable(tableName = TABLE_NAME_TO_OVERRIDE) + @DynamoDBTable(tableName = ORIGINAL_TABLE_NAME_TO_OVERRIDE) public static final class ExampleItem { private String partitionAttribute; private int sortAttribute; diff --git a/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java b/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java index 4dbc30b6..1b52ba3b 100644 --- a/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java +++ b/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java @@ -3,9 +3,9 @@ package com.amazonaws.examples; +import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.ORIGINAL_TABLE_NAME_TO_OVERRIDE; import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.PARTITION_ATTRIBUTE; import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.SORT_ATTRIBUTE; -import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.TABLE_NAME_TO_OVERRIDE; import static com.amazonaws.examples.TestUtils.US_WEST_2; import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID; import static com.amazonaws.examples.TestUtils.createDDBTable; @@ -26,7 +26,7 @@ public void testEncryptAndDecrypt() throws GeneralSecurityException { final AmazonDynamoDB ddb = DynamoDBEmbedded.create(); // Create the table under test - createDDBTable(ddb, TABLE_NAME_TO_OVERRIDE, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); + createDDBTable(ddb, ORIGINAL_TABLE_NAME_TO_OVERRIDE, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE); EncryptionContextOverridesWithDynamoDBMapper.encryptRecord( US_WEST_2_KEY_ID, OVERRIDE_TABLE_NAME, ddb, kms); diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java index e9adbd28..9fffa585 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/utils/EncryptionContextOperators.java @@ -26,25 +26,32 @@ private EncryptionContextOperators() {} /** * An operator for overriding EncryptionContext's table name for a specific DynamoDBEncryptor. If - * any table names or the encryption context itself is null, then it returns the original - * EncryptionContext. + * any table names or the encryption context is null, it returns the original EncryptionContext. * - * @param originalTableName the name of the table that should be overridden in the Encryption - * Context - * @param newTableName the table name that should be used in the Encryption Context + *

The client automatically adds the current table name to the encryption context so it's bound + * to the ciphertext. Use this method when the encryption context of encrypted table items + * includes a different table name, such as when a table is backed up, or table items are + * moved/copied to a different table. If you don't override the name of the current table with the + * table name in the encryption context, decrypt fails. This override affects the encryption + * context of all table items, including newly encrypted items. + * + * @param originalTableName Use this table name in the encryption context + * @param currentTableName Override this table name in the encryption context * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name */ public static UnaryOperator overrideEncryptionContextTableName( - String originalTableName, String newTableName) { + String originalTableName, String currentTableName) { return encryptionContext -> { if (encryptionContext == null || encryptionContext.getTableName() == null || originalTableName == null - || newTableName == null) { + || currentTableName == null) { return encryptionContext; } if (originalTableName.equals(encryptionContext.getTableName())) { - return new EncryptionContext.Builder(encryptionContext).withTableName(newTableName).build(); + return new EncryptionContext.Builder(encryptionContext) + .withTableName(currentTableName) + .build(); } else { return encryptionContext; }