1
1
package software .amazon .cryptography .dbencryptionsdk .dynamodb .enhancedclient ;
2
2
3
- import java .util .*;
4
- import java .util .stream .Collectors ;
3
+ import java .util .Collections ;
4
+ import java .util .HashMap ;
5
+ import java .util .HashSet ;
6
+ import java .util .List ;
7
+ import java .util .Map ;
8
+ import java .util .Objects ;
9
+ import java .util .Optional ;
10
+ import java .util .Set ;
5
11
6
12
import software .amazon .awssdk .enhanced .dynamodb .IndexMetadata ;
7
13
import software .amazon .awssdk .enhanced .dynamodb .KeyAttributeMetadata ;
@@ -23,9 +29,8 @@ public class DynamoDbEnhancedClientEncryption {
23
29
public static DynamoDbEncryptionInterceptor CreateDynamoDbEncryptionInterceptor (
24
30
CreateDynamoDbEncryptionInterceptorInput input ) {
25
31
Map <String , DynamoDbTableEncryptionConfig > tableConfigs = new HashMap <>();
26
- for (String tableName : input .tableEncryptionConfigs ().keySet ()) {
27
- tableConfigs .put (tableName , getTableConfig (input .tableEncryptionConfigs ().get (tableName )));
28
- }
32
+ input .tableEncryptionConfigs ().forEach (
33
+ (name , config ) -> tableConfigs .put (name , getTableConfig (config , name )));
29
34
30
35
return DynamoDbEncryptionInterceptor .builder ()
31
36
.config (DynamoDbTablesEncryptionConfig .builder ()
@@ -37,42 +42,50 @@ public static DynamoDbEncryptionInterceptor CreateDynamoDbEncryptionInterceptor(
37
42
private static Set <String > attributeNamesUsedInIndices (
38
43
final TableMetadata tableMetadata
39
44
) {
40
- Set <String > partitionAttributeNames = tableMetadata .indices ().stream ()
45
+ Set <String > allIndexAttributes = new HashSet <>();
46
+ tableMetadata .indices ().stream ()
41
47
.map (IndexMetadata ::partitionKey )
42
48
.filter (Optional ::isPresent )
43
49
.map (Optional ::get )
44
50
.map (KeyAttributeMetadata ::name )
45
- .collect ( Collectors . toSet () );
46
- Set < String > sortAttributeNames = tableMetadata .indices ().stream ()
51
+ .forEach ( allIndexAttributes :: add );
52
+ tableMetadata .indices ().stream ()
47
53
.map (IndexMetadata ::sortKey )
48
54
.filter (Optional ::isPresent )
49
55
.map (Optional ::get )
50
56
.map (KeyAttributeMetadata ::name )
51
- .collect (Collectors .toSet ());
52
- Set <String > allIndexAttributes = new HashSet <>();
53
- allIndexAttributes .addAll (partitionAttributeNames );
54
- allIndexAttributes .addAll (sortAttributeNames );
57
+ .forEach (allIndexAttributes ::add );
55
58
return allIndexAttributes ;
56
59
}
57
60
58
- private static DynamoDbTableEncryptionConfig getTableConfig (DynamoDbEnhancedTableEncryptionConfig configWithSchema ) {
61
+ private static DynamoDbTableEncryptionConfig getTableConfig (
62
+ final DynamoDbEnhancedTableEncryptionConfig configWithSchema ,
63
+ final String tableName
64
+ ) {
59
65
Map <String , CryptoAction > actions = new HashMap <>();
60
66
61
- Set <String > signOnlyAttributes = configWithSchema .schemaOnEncrypt ().tableMetadata ().customMetadataObject (CUSTOM_DDB_ENCRYPTION_SIGN_ONLY_PREFIX , Set .class ).orElseGet (HashSet ::new );
62
- Set <String > doNothingAttributes = configWithSchema .schemaOnEncrypt ().tableMetadata ().customMetadataObject (CUSTOM_DDB_ENCRYPTION_DO_NOTHING_PREFIX , Set .class ).orElseGet (HashSet ::new );
63
- Set <String > keyAttributes = attributeNamesUsedInIndices (configWithSchema .schemaOnEncrypt ().tableMetadata ());
67
+ TableSchema <?> topTableSchema = configWithSchema .schemaOnEncrypt ();
68
+ Set <String > signOnlyAttributes = getSignOnlyAttributes (topTableSchema );
69
+ Set <String > doNothingAttributes = getDoNothingAttributes (topTableSchema );
70
+ Set <String > keyAttributes = attributeNamesUsedInIndices (topTableSchema .tableMetadata ());
64
71
65
72
if (!Collections .disjoint (keyAttributes , doNothingAttributes )) {
66
73
throw DynamoDbEncryptionException .builder ()
67
- .message ("Cannot use @DynamoDbEncryptionDoNothing on primary key attributes." )
74
+ .message (String .format (
75
+ "Cannot use @DynamoDbEncryptionDoNothing on primary key attributes. Found on Table Name: %s" ,
76
+ tableName ))
68
77
.build ();
69
78
} else if (!Collections .disjoint (signOnlyAttributes , doNothingAttributes )) {
70
79
throw DynamoDbEncryptionException .builder ()
71
- .message ("Cannot use @DynamoDbEncryptionDoNothing and @DynamoDbEncryptionSignOnly on same attribute." )
80
+ .message (String .format (
81
+ "Cannot use @DynamoDbEncryptionDoNothing and @DynamoDbEncryptionSignOnly on same attribute. Found on Table Name: %s" ,
82
+ tableName ))
72
83
.build ();
73
84
}
74
85
75
- List <String > attributeNames = configWithSchema .schemaOnEncrypt ().attributeNames ();
86
+ List <String > attributeNames = topTableSchema .attributeNames ();
87
+ StringBuilder path = new StringBuilder ();
88
+ path .append (tableName ).append ("." );
76
89
for (String attributeName : attributeNames ) {
77
90
if (keyAttributes .contains (attributeName )) {
78
91
// key attributes are always SIGN_ONLY
@@ -87,14 +100,14 @@ private static DynamoDbTableEncryptionConfig getTableConfig(DynamoDbEnhancedTabl
87
100
}
88
101
89
102
// Detect Encryption Flags that are Ignored b/c they are in a Nested Class
90
- scanForIgnoredEncryptionTagsShallow ( configWithSchema , attributeName );
103
+ scanForIgnoredEncryptionTags ( topTableSchema , attributeName , path );
91
104
}
92
105
93
106
DynamoDbTableEncryptionConfig .Builder builder = DynamoDbTableEncryptionConfig .builder ();
94
- String partitionName = configWithSchema . schemaOnEncrypt () .tableMetadata ().primaryPartitionKey ();
107
+ String partitionName = topTableSchema .tableMetadata ().primaryPartitionKey ();
95
108
builder = builder .partitionKeyName (partitionName );
96
109
97
- Optional <String > sortName = configWithSchema . schemaOnEncrypt () .tableMetadata ().primarySortKey ();
110
+ Optional <String > sortName = topTableSchema .tableMetadata ().primarySortKey ();
98
111
if (sortName .isPresent ()) {
99
112
builder = builder .sortKeyName (sortName .get ());
100
113
}
@@ -122,10 +135,22 @@ private static DynamoDbTableEncryptionConfig getTableConfig(DynamoDbEnhancedTabl
122
135
.build ();
123
136
}
124
137
138
+ @ SuppressWarnings ("unchecked" )
139
+ private static Set <String > getSignOnlyAttributes (TableSchema <?> tableSchema ) {
140
+ return tableSchema .tableMetadata ()
141
+ .customMetadataObject (CUSTOM_DDB_ENCRYPTION_SIGN_ONLY_PREFIX , Set .class )
142
+ .orElseGet (HashSet ::new );
143
+ }
144
+
145
+ @ SuppressWarnings ("unchecked" )
146
+ private static Set <String > getDoNothingAttributes (TableSchema <?> tableSchema ) {
147
+ return tableSchema .tableMetadata ()
148
+ .customMetadataObject (CUSTOM_DDB_ENCRYPTION_DO_NOTHING_PREFIX , Set .class )
149
+ .orElseGet (HashSet ::new );
150
+ }
151
+
125
152
/**
126
153
* Detects DynamoDB Encryption Tags in Nested Enhanced Types.<p>
127
- * This method ONLY parses ONE Layer of nesting.<p>
128
- * It does NOT traverse further nested Enhanced Types.<p>
129
154
* DynamoDB Encryption Tags in Nested Classes are IGNORED by the
130
155
* Database Encryption SDK for DynamoDB.<p>
131
156
* As such, Detection of a nested DynamoDB Encryption Tag on a Nested Type
@@ -138,30 +163,42 @@ private static DynamoDbTableEncryptionConfig getTableConfig(DynamoDbEnhancedTabl
138
163
* as any Encryption Tag on the field that will be "flattened" is ignored.<p>
139
164
* This method DOES NOT detect these "ignored-by-flattened" tags.
140
165
*/
141
- private static void scanForIgnoredEncryptionTagsShallow (
142
- final DynamoDbEnhancedTableEncryptionConfig configWithSchema ,
143
- final String attributeName
166
+ private static void scanForIgnoredEncryptionTags (
167
+ final TableSchema <?> tableSchema ,
168
+ final String attributeName ,
169
+ final StringBuilder path
144
170
) {
145
- AttributeConverter attributeConverter = configWithSchema .schemaOnEncrypt ().converterForAttribute (attributeName );
171
+ AttributeConverter <?> attributeConverter = tableSchema .converterForAttribute (attributeName );
172
+ StringBuilder attributePath = new StringBuilder (path ).append (attributeName ).append ("." );
146
173
if (
147
174
Objects .nonNull (attributeConverter ) &&
148
175
Objects .nonNull (attributeConverter .type ()) &&
149
176
attributeConverter .type ().tableSchema ().isPresent ()
150
177
) {
151
- Object maybeTableSchema = attributeConverter .type ().tableSchema ().get ();
152
- if (maybeTableSchema instanceof TableSchema ) {
153
- TableSchema subTableSchema = (TableSchema ) maybeTableSchema ;
154
- if (
155
- subTableSchema .tableMetadata ().customMetadata ().containsKey (CUSTOM_DDB_ENCRYPTION_SIGN_ONLY_PREFIX ) ||
156
- subTableSchema .tableMetadata ().customMetadata ().containsKey (CUSTOM_DDB_ENCRYPTION_DO_NOTHING_PREFIX )
157
- ) {
158
- throw DynamoDbEncryptionException .builder ()
159
- .message (String .format (
160
- "Detected a DynamoDbEncryption Tag/Configuration on a nested attribute of %s. " +
161
- "This is NOT Supported at this time!" ,
162
- attributeName ))
163
- .build ();
164
- }
178
+ TableSchema <?> subTableSchema = attributeConverter .type ().tableSchema ().get ();
179
+ Set <String > signOnlyAttributes = getSignOnlyAttributes (subTableSchema );
180
+ if (signOnlyAttributes .size () > 0 ) {
181
+ throw DynamoDbEncryptionException .builder ()
182
+ .message (String .format (
183
+ "Detected DynamoDbEncryption Tag %s on a nested attribute with Path %s. " +
184
+ "This is NOT Supported at this time!" ,
185
+ CUSTOM_DDB_ENCRYPTION_SIGN_ONLY_PREFIX ,
186
+ attributePath .append (signOnlyAttributes .toArray ()[0 ])))
187
+ .build ();
188
+ }
189
+ Set <String > doNothingAttributes = getDoNothingAttributes (subTableSchema );
190
+ if (doNothingAttributes .size () > 0 ) {
191
+ throw DynamoDbEncryptionException .builder ()
192
+ .message (String .format (
193
+ "Detected DynamoDbEncryption Tag %s on a nested attribute with Path %s. " +
194
+ "This is NOT Supported at this time!" ,
195
+ CUSTOM_DDB_ENCRYPTION_DO_NOTHING_PREFIX ,
196
+ attributePath .append (doNothingAttributes .toArray ()[0 ])))
197
+ .build ();
198
+ }
199
+ List <String > subAttributeNames = subTableSchema .attributeNames ();
200
+ for (String subAttributeName : subAttributeNames ) {
201
+ scanForIgnoredEncryptionTags (subTableSchema , subAttributeName , attributePath );
165
202
}
166
203
}
167
204
}
0 commit comments