diff --git a/.github/workflows/ci_test_net.yml b/.github/workflows/ci_test_net.yml index b5dd6f205..d0c6a3b70 100644 --- a/.github/workflows/ci_test_net.yml +++ b/.github/workflows/ci_test_net.yml @@ -26,7 +26,8 @@ on: jobs: testDotNet: # Don't run the nightly build on forks - if: github.event_name != 'schedule' || github.repository_owner == 'awslabs' + # Disabled until we reintroduce DynamoDbEncryption, since a matrix vector cannot be empty + if: false && (github.event_name != 'schedule' || github.repository_owner == 'awslabs') strategy: matrix: library: [ diff --git a/DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryption.java b/DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryption.java index f1cbf4a59..16daf0d28 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryption.java +++ b/DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryption.java @@ -3,13 +3,16 @@ import java.util.*; import java.util.stream.Collectors; +import software.amazon.awssdk.enhanced.dynamodb.IndexMetadata; +import software.amazon.awssdk.enhanced.dynamodb.KeyAttributeMetadata; +import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; import software.amazon.cryptography.dbencryptionsdk.dynamodb.DynamoDbEncryptionInterceptor; import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbEncryptionException; -import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTableEncryptionConfig; import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTablesEncryptionConfig; +import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTableEncryptionConfig; import software.amazon.cryptography.dbencryptionsdk.structuredencryption.model.CryptoAction; @@ -31,11 +34,33 @@ public static DynamoDbEncryptionInterceptor CreateDynamoDbEncryptionInterceptor( .build(); } + private static Set attributeNamesUsedInIndices( + final TableMetadata tableMetadata + ) { + Set partitionAttributeNames = tableMetadata.indices().stream() + .map(IndexMetadata::partitionKey) + .filter(Optional::isPresent) + .map(Optional::get) + .map(KeyAttributeMetadata::name) + .collect(Collectors.toSet()); + Set sortAttributeNames = tableMetadata.indices().stream() + .map(IndexMetadata::sortKey) + .filter(Optional::isPresent) + .map(Optional::get) + .map(KeyAttributeMetadata::name) + .collect(Collectors.toSet()); + Set allIndexAttributes = new HashSet<>(); + allIndexAttributes.addAll(partitionAttributeNames); + allIndexAttributes.addAll(sortAttributeNames); + return allIndexAttributes; + } + private static DynamoDbTableEncryptionConfig getTableConfig(DynamoDbEnhancedTableEncryptionConfig configWithSchema) { Map actions = new HashMap<>(); + Set signOnlyAttributes = configWithSchema.schemaOnEncrypt().tableMetadata().customMetadataObject(CUSTOM_DDB_ENCRYPTION_SIGN_ONLY_PREFIX, Set.class).orElseGet(HashSet::new); Set doNothingAttributes = configWithSchema.schemaOnEncrypt().tableMetadata().customMetadataObject(CUSTOM_DDB_ENCRYPTION_DO_NOTHING_PREFIX, Set.class).orElseGet(HashSet::new); - Set keyAttributes = configWithSchema.schemaOnEncrypt().tableMetadata().keyAttributes().stream().map(val -> val.name()).collect(Collectors.toSet()); + Set keyAttributes = attributeNamesUsedInIndices(configWithSchema.schemaOnEncrypt().tableMetadata()); if (!Collections.disjoint(keyAttributes, doNothingAttributes)) { throw DynamoDbEncryptionException.builder() @@ -61,6 +86,7 @@ private static DynamoDbTableEncryptionConfig getTableConfig(DynamoDbEnhancedTabl actions.put(attributeName, CryptoAction.ENCRYPT_AND_SIGN); } + // Detect Encryption Flags that are Ignored b/c they are in a Nested Class scanForIgnoredEncryptionTagsShallow(configWithSchema, attributeName); } @@ -103,7 +129,14 @@ private static DynamoDbTableEncryptionConfig getTableConfig(DynamoDbEnhancedTabl * DynamoDB Encryption Tags in Nested Classes are IGNORED by the * Database Encryption SDK for DynamoDB.

* As such, Detection of a nested DynamoDB Encryption Tag on a Nested Type - * triggers a Runtime Exception that MUST NOT BE ignored. + * triggers a Runtime Exception that MUST NOT BE ignored.

+ * CAVEAT: Encryption Tags on fields of Nested Classes that are + * Flattened onto the top record are Respected.

+ * The behavior of Flatten pushes the Attributes onto the top level record, + * making the "flattened sub-fields" equivalent to any other DynamoDB Attribute.

+ * However, there still exists a possibility for IGNORED Encryption Tags, + * as any Encryption Tag on the field that will be "flattened" is ignored.

+ * This method DOES NOT detect these "ignored-by-flattened" tags. */ private static void scanForIgnoredEncryptionTagsShallow( final DynamoDbEnhancedTableEncryptionConfig configWithSchema, diff --git a/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEncryptionEnhancedClientIntegrationTests.java b/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEncryptionEnhancedClientIntegrationTests.java index 861f5ff5b..38aa32d11 100644 --- a/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEncryptionEnhancedClientIntegrationTests.java +++ b/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEncryptionEnhancedClientIntegrationTests.java @@ -187,53 +187,6 @@ public void TestPutAndGetAnnotatedConvertedBy() { assertEquals(result.getNestedEncrypted(), record.getNestedEncrypted()); assertEquals(result.getNestedSigned(), record.getNestedSigned()); } - - @Test - public void TestPutAndGetConflictingFlattenedBean() { - final String PARTITION = "ConflictingFlattenedBean"; - final int SORT = 20230713; - TableSchema schemaOnEncrypt = - TableSchema.fromBean(ConflictingFlattenedBean.class); - List allowedUnsignedAttributes = Arrays.asList( - "lastName", - "anotherLastName", - "finalLastName"); - DynamoDbEnhancedClient enhancedClient = - initEnhancedClientWithInterceptor(schemaOnEncrypt, allowedUnsignedAttributes, null, null); - - DynamoDbTable table = enhancedClient.table(TEST_TABLE_NAME, schemaOnEncrypt); - - ConflictingFlattenedBean record = new ConflictingFlattenedBean(); - record.setPartitionKey(PARTITION); - record.setSortKey(SORT); - record.setNestedEncrypted(new ConflictingFlattenedBean.NestedBean ( - "9305B367-C477-4A58-9E6C-BF7D59D17C8A", "Moe", "Howard" - )); - record.setNestedIgnored(new ConflictingFlattenedBean.FinalNestedBean ( - "9305B367-C477-4A58-9E6C-BF7D59D17C8A", "Larry", "Fine" - )); - record.setNestedSigned(new ConflictingFlattenedBean.AnotherNestedBean ( - "9305B367-C477-4A58-9E6C-BF7D59D17C8A", "Curly", "Howard" - )); - - // Put an item into an Amazon DynamoDB table. - table.putItem(record); - // table.deleteItem(record); - - // Get the item back from the table - Key key = Key.builder() - .partitionValue(PARTITION).sortValue(SORT) - .build(); - - // Get the item by using the key. - ConflictingFlattenedBean result = table.getItem( - (GetItemEnhancedRequest.Builder requestBuilder) -> requestBuilder.key(key)); - assertEquals(result.getPartitionKey(), record.getPartitionKey()); - assertEquals(result.getSortKey(), record.getSortKey()); - assertEquals(result.getNestedIgnored(), record.getNestedIgnored()); - assertEquals(result.getNestedEncrypted(), record.getNestedEncrypted()); - assertEquals(result.getNestedSigned(), record.getNestedSigned()); - } @Test public void TestPutAndGetSignOnly() { diff --git a/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryptionTest.java b/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryptionTest.java index 19868adf5..8f476ac34 100644 --- a/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryptionTest.java +++ b/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryptionTest.java @@ -319,28 +319,13 @@ public void TestConflictingAnnotatedNestedBean() { } @Test( - // This test SHOULD yield an exception, but does not, at this time. - // expectedExceptions = DynamoDbEncryptionException.class - ) - public void TestInvalidAnnotatedConvertedByAnnotationsOnNonAttributes() { - TableSchema schemaOnEncrypt = - TableSchema.fromBean(InvalidAnnotatedConvertedBy.class); - Map tableConfigs = new HashMap<>(); - tableConfigs.put(TEST_TABLE_NAME, - DynamoDbEnhancedTableEncryptionConfig.builder() - .logicalTableName(TEST_TABLE_NAME) - .keyring(createKmsKeyring()) - .schemaOnEncrypt(schemaOnEncrypt) - .build()); - DynamoDbEnhancedClientEncryption.CreateDynamoDbEncryptionInterceptor( - CreateDynamoDbEncryptionInterceptorInput.builder() - .tableEncryptionConfigs(tableConfigs) - .build()); - } - - @Test( - // This test SHOULD yield an exception, but does not, at this time. - // expectedExceptions = DynamoDbEncryptionException.class + // We skip this Test. + enabled = false, + // The DB-ESDK-DynamoDB for Java SHOULD detect ALL DynamoDBEncryption + // Tags & Attributes that are IGNORED and throw an Exception. + // However, detecting IGNORED DynamoDBEncryption Tags & Attributes + // when a nested class is Flattened has NOT been implemented. + expectedExceptions = DynamoDbEncryptionException.class ) public void TestConflictingFlattenedBean() { TableSchema schemaOnEncrypt = diff --git a/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/InvalidAnnotatedConvertedBy.java b/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/InvalidAnnotatedConvertedBy.java deleted file mode 100644 index d7369aabd..000000000 --- a/DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/InvalidAnnotatedConvertedBy.java +++ /dev/null @@ -1,129 +0,0 @@ -package software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient; - -import java.util.HashMap; -import java.util.Map; - -import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter; -import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; -import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; -import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute; -import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; -import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbConvertedBy; -import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; -import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * This is an INVALID use of DynamoDbEncryption annotations on nested attributes. - * The DynamoDbEncryption annotations are placed on elements that are NOT - * DynamoDB Attributes but that will be mapped together into one attribute.

- */ -@DynamoDbBean -public class InvalidAnnotatedConvertedBy { - private String partitionKey; - private int sortKey; - private ConvertedByNestedBean nestedBeanClass; - - @DynamoDbPartitionKey - @DynamoDbAttribute(value = "partition_key") - public String getPartitionKey() { - return this.partitionKey; - } - - public void setPartitionKey(String partitionKey) { - this.partitionKey = partitionKey; - } - - @DynamoDbSortKey - @DynamoDbAttribute(value = "sort_key") - public int getSortKey() { - return this.sortKey; - } - - public void setSortKey(int sortKey) { - this.sortKey = sortKey; - } - - @DynamoDbConvertedBy(ConvertedByNestedBean.NestedBeanConverter.class) - @DynamoDbAttribute("nestedEncrypted") - public ConvertedByNestedBean getNestedBeanClass() { - return this.nestedBeanClass; - } - - public void setNestedBeanClass(ConvertedByNestedBean nestedBeanClass) { - this.nestedBeanClass = nestedBeanClass; - } - - public static class ConvertedByNestedBean { - private String id; - private String firstName; - private String lastName; - - public ConvertedByNestedBean() {}; - - public ConvertedByNestedBean(String id, String firstName, String lastName) { - this.id = id; - this.firstName = firstName; - this.lastName = lastName; - } - - public String getId() { return this.id; } - public void setId(String id) { this.id = id; } - - @DynamoDbEncryptionSignOnly //This annotation is IGNORED - public String getFirstName() { return firstName; } - public void setFirstName(String firstName) { this.firstName = firstName; } - - @DynamoDbEncryptionDoNothing //This annotation is IGNORED - public String getLastName() { return lastName; } - public void setLastName(String lastName) { this.lastName = lastName; } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - ConvertedByNestedBean other = (ConvertedByNestedBean) obj; - if (id == null) { - if (other.getId() != null) return false; - } else if (!id.equals(other.getId())) return false; - if (firstName == null) { - if (other.getFirstName() != null) return false; - } else if (!firstName.equals(other.getFirstName())) return false; - if (lastName == null) { - if (other.getLastName() != null) return false; - } else if (!lastName.equals(other.getLastName())) return false; - return true; - } - - public static final class NestedBeanConverter implements AttributeConverter { - @Override - public AttributeValue transformFrom(ConvertedByNestedBean ConvertedByNestedBean) { - Map map = new HashMap<>(3); - map.put("id", AttributeValue.fromS(ConvertedByNestedBean.getId())); - map.put("firstName", AttributeValue.fromS(ConvertedByNestedBean.getFirstName())); - map.put("lastName", AttributeValue.fromS(ConvertedByNestedBean.getLastName())); - return AttributeValue.fromM(map); - } - - @Override - public ConvertedByNestedBean transformTo(AttributeValue attributeValue) { - Map map = attributeValue.m(); - return new ConvertedByNestedBean( - map.get("id").s(), - map.get("firstName").s(), - map.get("lastName").s()); - } - - @Override - public EnhancedType type() { - return EnhancedType.of(ConvertedByNestedBean.class); - } - - @Override - public AttributeValueType attributeValueType() { - return AttributeValueType.M; - } - } - } -}