Skip to content

Commit 8928637

Browse files
ajewellamzseebees
authored andcommitted
feat: allow condition expressions without encrypted attributes (#70)
* feat: allow condition expressions without encrypted attributes * better error messages
1 parent 22e78d1 commit 8928637

File tree

6 files changed

+1216
-14
lines changed

6 files changed

+1216
-14
lines changed

DynamoDbEncryption/runtimes/java/src/test/java/software/aws/cryptography/dynamoDbEncryption/DynamoDbEncryptionInterceptorTest.java

+12-11
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public void TestPutItemEncryptsAccordingToAttributeActions() {
3939
PutItemRequest oldRequest = PutItemRequest.builder()
4040
.tableName(TEST_TABLE_NAME)
4141
.item(item)
42+
.conditionExpression(TEST_ATTR2_NAME + " < :a")
4243
.build();
4344

4445
Context.ModifyRequest context = InterceptorContext.builder()
@@ -100,10 +101,10 @@ public void TestInterceptorBuilderAcceptsLegacyEncryptor() {
100101
}
101102

102103
@Test
103-
public void TestPutItemGetItemWithConditionExpression() {
104+
public void TestPutItemGetItemWithConditionExpressionBad() {
104105
PutItemRequest oldRequest = PutItemRequest.builder()
105106
.tableName(TEST_TABLE_NAME)
106-
.conditionExpression("foo")
107+
.conditionExpression(TEST_ATTR_NAME + " < :a")
107108
.build();
108109

109110
Context.ModifyRequest context = InterceptorContext.builder()
@@ -117,7 +118,7 @@ public void TestPutItemGetItemWithConditionExpression() {
117118
Exception exception = assertThrows(DynamoDbEncryptionException.class, () -> {
118119
interceptor.modifyRequest(context, attributes);
119120
});
120-
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted tables"));
121+
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted attributes : attr1"));
121122
}
122123

123124
@Test
@@ -191,7 +192,7 @@ public void TestTransactWriteItemsWithConditionCheck() {
191192
.build())
192193
.conditionCheck(ConditionCheck.builder()
193194
.tableName(TEST_TABLE_NAME)
194-
.conditionExpression("foo")
195+
.conditionExpression(TEST_ATTR_NAME + " < :a")
195196
.build())
196197
.build())
197198
.build();
@@ -207,7 +208,7 @@ public void TestTransactWriteItemsWithConditionCheck() {
207208
Exception exception = assertThrows(DynamoDbEncryptionException.class, () -> {
208209
interceptor.modifyRequest(context, attributes);
209210
});
210-
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted tables"));
211+
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted attributes : attr1"));
211212
}
212213

213214
@Test
@@ -217,7 +218,7 @@ public void TestTransactWriteItemsWithPutConditionExpression() {
217218
TransactWriteItem.builder()
218219
.put(Put.builder()
219220
.tableName(TEST_TABLE_NAME)
220-
.conditionExpression("foo")
221+
.conditionExpression(TEST_ATTR_NAME + " < :a")
221222
.build())
222223
.build())
223224
.build();
@@ -233,7 +234,7 @@ public void TestTransactWriteItemsWithPutConditionExpression() {
233234
Exception exception = assertThrows(DynamoDbEncryptionException.class, () -> {
234235
interceptor.modifyRequest(context, attributes);
235236
});
236-
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted tables"));
237+
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted attributes : attr1"));
237238
}
238239

239240
@Test
@@ -243,7 +244,7 @@ public void TestTransactWriteItemsWithDeleteConditionExpression() {
243244
TransactWriteItem.builder()
244245
.delete(Delete.builder()
245246
.tableName(TEST_TABLE_NAME)
246-
.conditionExpression("foo")
247+
.conditionExpression(TEST_ATTR_NAME + " < :a")
247248
.build())
248249
.build())
249250
.build();
@@ -259,7 +260,7 @@ public void TestTransactWriteItemsWithDeleteConditionExpression() {
259260
Exception exception = assertThrows(DynamoDbEncryptionException.class, () -> {
260261
interceptor.modifyRequest(context, attributes);
261262
});
262-
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted tables"));
263+
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted attributes : attr1"));
263264
}
264265

265266
@Test
@@ -318,7 +319,7 @@ public void TestTransactWriteItemsWithUpdateOnEncryptedTableBad() {
318319
public void TestDeleteItemWithConditionExpression() {
319320
DeleteItemRequest oldRequest = DeleteItemRequest.builder()
320321
.tableName(TEST_TABLE_NAME)
321-
.conditionExpression("foo")
322+
.conditionExpression(TEST_ATTR_NAME + " < :a")
322323
.build();
323324

324325
Context.ModifyRequest context = InterceptorContext.builder()
@@ -332,7 +333,7 @@ public void TestDeleteItemWithConditionExpression() {
332333
Exception exception = assertThrows(DynamoDbEncryptionException.class, () -> {
333334
interceptor.modifyRequest(context, attributes);
334335
});
335-
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted tables"));
336+
assertTrue(exception.getMessage().contains("Condition Expressions forbidden on encrypted attributes : attr1"));
336337
}
337338

338339
@Test

DynamoDbItemEncryptor/src/AwsCryptographyDynamoDbItemEncryptorOperations.dfy

+31
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,37 @@ module AwsCryptographyDynamoDbItemEncryptorOperations refines AbstractAwsCryptog
8080
!AllowedUnauthenticated(unauthenticatedAttributes, unauthenticatedPrefix, attribute)
8181
}
8282

83+
function method CryptoActionString(action: CSE.CryptoAction) : string
84+
{
85+
match action {
86+
case DO_NOTHING => "DO_NOTHING"
87+
case SIGN_ONLY => "SIGN_ONLY"
88+
case ENCRYPT_AND_SIGN => "ENCRYPT_AND_SIGN"
89+
}
90+
}
91+
92+
function method ExplainNotForwardCompatible(
93+
attr: string,
94+
action: CSE.CryptoAction,
95+
unauthenticatedAttributes: Option<ComAmazonawsDynamodbTypes.AttributeNameList>,
96+
unauthenticatedPrefix: Option<string>
97+
)
98+
: string
99+
requires !ForwardCompatibleAttributeAction(attr, action, unauthenticatedAttributes, unauthenticatedPrefix)
100+
{
101+
"Attribute " + attr + " is configured as " + CryptoActionString(action) + " but " +
102+
if action == CSE.DO_NOTHING then
103+
"it must also be in unauthenticatedAttributes or begin with the unauthenticatedPrefix."
104+
else if unauthenticatedAttributes.Some? && attr in unauthenticatedAttributes.value then
105+
"it is also in unauthenticatedAttributes."
106+
else if unauthenticatedPrefix.Some? && unauthenticatedPrefix.value <= attr then
107+
"it also begins with the unauthenticatedPrefix."
108+
else
109+
assert SE.ReservedPrefix <= attr;
110+
"it also begins with the reserved prefix."
111+
}
112+
113+
83114
// Is this attribute unknown to the config?
84115
predicate method UnknownAttribute(config : InternalConfig, attr : ComAmazonawsDynamodbTypes.AttributeName)
85116
{

DynamoDbItemEncryptor/src/DDBSupport.dfy

+21-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ include "AwsCryptographyDynamoDbItemEncryptorOperations.dfy"
1313
include "Util.dfy"
1414
include "VirtualDDB.dfy"
1515
include "UpdateExpr.dfy"
16+
include "FilterExpr.dfy"
1617

1718
module DynamoDBSupport {
1819

@@ -30,6 +31,7 @@ module DynamoDBSupport {
3031
import SE = StructuredEncryptionUtil
3132
import Update = DynamoDbUpdateExpr
3233
import SET = AwsCryptographyStructuredEncryptionTypes
34+
import Filter = DynamoDBFilterExpr
3335

3436
// IsWritable examines an AttributeMap and fails if it is unsuitable for writing.
3537
// At the moment, this means that no attribute names starts with "aws_dbe_",
@@ -49,6 +51,20 @@ module DynamoDBSupport {
4951
Failure("Writing reserved attributes not allowed : " + Join(badSeq, "\n"))
5052
}
5153

54+
function method GetEncryptedAttributes(
55+
config : Config,
56+
expr : Option<string>,
57+
attrNames : Option<DDB.ExpressionAttributeNameMap> )
58+
: seq<string>
59+
{
60+
if expr.None? then
61+
[]
62+
else
63+
var attrs := Filter.ExtractAttributes(expr.value, attrNames);
64+
Seq.Filter((attr : string) => IsEncrypted(config, attr), attrs)
65+
}
66+
67+
5268
// TestConditionExpression fails if a condition expression is not suitable for the
5369
// given encryption schema.
5470
// Generally this means no encrypted attribute is referenced.
@@ -61,7 +77,11 @@ module DynamoDBSupport {
6177
: Result<bool, string>
6278
{
6379
if expr.Some? then
64-
Failure("Condition Expressions forbidden on encrypted tables")
80+
var attrs := GetEncryptedAttributes(config, expr, attrNames);
81+
if |attrs| == 0 then
82+
Success(true)
83+
else
84+
Failure("Condition Expressions forbidden on encrypted attributes : " + Join(attrs, ","))
6585
else
6686
Success(true)
6787
}

0 commit comments

Comments
 (0)