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; + } }