Filter for username constraint events where confirmed=true
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
Katherine 2026-03-04 16:37:54 -05:00 committed by GitHub
parent 79d41786a6
commit 40b329a88c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 338 additions and 28 deletions

View File

@ -88,20 +88,22 @@ public abstract class AbstractUpdatesHandler<T> implements
private KinesisRecord<T> dbUpdateFor(final StreamRecord dbRecord) {
Map<String, AttributeValue> oldImage = dbRecord.getOldImage();
Map<String, AttributeValue> newImage = dbRecord.getNewImage();
if (oldImage == null || oldImage.isEmpty()) {
return toKinesisRecord(null, fromDynamoDbImage(newImage));
} else if (newImage == null || newImage.isEmpty()) {
return toKinesisRecord(fromDynamoDbImage(oldImage), null);
}
T oldConstraint = fromDynamoDbImage(oldImage);
T newConstraint = fromDynamoDbImage(newImage);
if (oldConstraint.equals(newConstraint)) {
final T prev = oldImage == null || oldImage.isEmpty() ? null : fromDynamoDbImage(oldImage);
final T next = newImage == null || newImage.isEmpty() ? null : fromDynamoDbImage(newImage);
if (prev == null && next == null) {
return null;
} else {
return toKinesisRecord(oldConstraint, newConstraint);
}
if (prev != null && prev.equals(next)) {
return null;
}
return toKinesisRecord(prev, next);
}
@Nullable
abstract T fromDynamoDbImage(Map<String, AttributeValue> image);
abstract KinesisRecord<T> toKinesisRecord(@Nullable T prev, @Nullable T next);
}

View File

@ -26,8 +26,15 @@ public class FilterUsernameUpdatesHandler extends AbstractUpdatesHandler<Usernam
super(kinesisClient, kinesisOutputStream);
}
@Nullable
UsernameConstraint fromDynamoDbImage(final Map<String, AttributeValue> image) {
return UsernameConstraint.fromItem(image);
final UsernameConstraint constraint = UsernameConstraint.fromItem(image);
// We only care about confirmed username constraint entries since those indicate a change on the account
// that must be propagated to the transparency log.
if (!constraint.confirmed()) {
return null;
}
return constraint;
}
KinesisRecord<UsernameConstraint> toKinesisRecord(final @Nullable UsernameConstraint prev, final @Nullable UsernameConstraint next) {

View File

@ -16,12 +16,15 @@ import java.util.Map;
record UsernameConstraint(
byte[] usernameHash,
byte[] aci) {
byte[] aci,
boolean confirmed) {
@VisibleForTesting
static final String KEY_USERNAME_HASH = "N";
@VisibleForTesting
static final String ATTR_ACCOUNT_UUID = "U";
@VisibleForTesting
static final String ATTR_CONFIRMED = "F";
@Override
public boolean equals(final Object o) {
@ -31,7 +34,8 @@ record UsernameConstraint(
return false;
UsernameConstraint usernameConstraint = (UsernameConstraint) o;
return Arrays.equals(usernameHash, usernameConstraint.usernameHash) &&
Arrays.equals(aci, usernameConstraint.aci);
Arrays.equals(aci, usernameConstraint.aci) &&
confirmed == usernameConstraint.confirmed;
}
public record Pair(@Nullable UsernameConstraint prev, @Nullable UsernameConstraint next) implements KinesisRecord<UsernameConstraint> {
@ -51,6 +55,7 @@ record UsernameConstraint(
static UsernameConstraint fromItem(Map<String, AttributeValue> item) {
Preconditions.checkNotNull(item.get(KEY_USERNAME_HASH));
Preconditions.checkNotNull(item.get(ATTR_ACCOUNT_UUID));
Preconditions.checkNotNull(item.get(ATTR_CONFIRMED));
final byte[] usernameHash = new byte[32];
@ -60,6 +65,8 @@ record UsernameConstraint(
final byte[] uuid = new byte[16];
item.get(ATTR_ACCOUNT_UUID).getB().get(uuid);
return new UsernameConstraint(usernameHash, uuid);
final boolean confirmed = item.get(ATTR_CONFIRMED).getBOOL();
return new UsernameConstraint(usernameHash, uuid, confirmed);
}
}

View File

@ -57,22 +57,44 @@ class FilterUsernameUpdatesHandlerTest {
private static Stream<Arguments> handleRequest() {
return Stream.of(
Arguments.of(
"username/testevent_creation.json",
new UsernameConstraint.Pair(
null,
new UsernameConstraint(USERNAME_HASH, PREV_ACI))),
Arguments.of(
"username/testevent_deletion.json",
new UsernameConstraint.Pair(
new UsernameConstraint(USERNAME_HASH, PREV_ACI),
"username/testevent_account_deletion.json", new UsernameConstraint.Pair(
new UsernameConstraint(USERNAME_HASH, PREV_ACI, true),
null)),
Arguments.of(
"username/testevent_nochange.json", null),
"username/testevent_create_confirm.json", new UsernameConstraint.Pair(
null,
new UsernameConstraint(USERNAME_HASH, NEXT_ACI, true))),
Arguments.of(
"username/testevent_modify.json",
"username/testevent_create_reserve.json", null),
Arguments.of(
"username/testevent_deletion_hold_expiring.json", null),
Arguments.of(
"username/testevent_modify_confirm.json",
new UsernameConstraint.Pair(
new UsernameConstraint(USERNAME_HASH, PREV_ACI),
new UsernameConstraint(USERNAME_HASH, NEXT_ACI))));
null,
new UsernameConstraint(USERNAME_HASH, NEXT_ACI, true))),
Arguments.of(
"username/testevent_modify_unconfirm.json",
new UsernameConstraint.Pair(
new UsernameConstraint(USERNAME_HASH, PREV_ACI, true),
null)),
Arguments.of(
"username/testevent_modify_aci_changed_old_confirmed_new_confirmed.json",
new UsernameConstraint.Pair(
new UsernameConstraint(USERNAME_HASH, PREV_ACI, true),
new UsernameConstraint(USERNAME_HASH, NEXT_ACI, true))),
Arguments.of(
"username/testevent_modify_aci_changed_old_unconfirmed_new_confirmed.json",
new UsernameConstraint.Pair(
null,
new UsernameConstraint(USERNAME_HASH, NEXT_ACI, true))),
Arguments.of(
"username/testevent_modify_aci_changed_old_confirmed_new_unconfirmed.json",
new UsernameConstraint.Pair(
new UsernameConstraint(USERNAME_HASH, PREV_ACI, true),
null)),
Arguments.of(
"username/testevent_nochange.json", null));
}
UsernameConstraint.Pair mapWithoutException(SdkBytes in) {

View File

@ -14,6 +14,9 @@
}
},
"OldImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
@ -25,5 +28,5 @@
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
]
}

View File

@ -14,11 +14,14 @@
}
},
"NewImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "IiIiIiIiIiIiIiIiIiIiIg=="
"B": "BbBbBbBbBbBbBbBbBbBbBg=="
}
},
"SizeBytes": 148,

View File

@ -0,0 +1,35 @@
{
"Records": [
{
"eventID": "88888888-85b6-4a4b-a74e-bf4688888888",
"eventName": "MODIFY",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": "2026-02-17T12:55:17-05:00",
"Keys": {
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
}
},
"NewImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "IiIiIiIiIiIiIiIiIiIiIg=="
},
"TTL": {
"N": "123456789"
}
},
"SizeBytes": 148,
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
}

View File

@ -0,0 +1,35 @@
{
"Records": [
{
"eventID": "88888888-85b6-4a4b-a74e-bf4688888888",
"eventName": "REMOVE",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": "2026-02-17T12:55:15-05:00",
"Keys": {
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
}
},
"OldImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "IiIiIiIiIiIiIiIiIiIiIg=="
},
"TTL": {
"N": "123456789"
}
},
"SizeBytes": 96,
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
}

View File

@ -14,6 +14,9 @@
}
},
"NewImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
@ -22,6 +25,9 @@
}
},
"OldImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},

View File

@ -0,0 +1,46 @@
{
"Records": [
{
"eventID": "88888888-85b6-4a4b-a74e-bf4688888888",
"eventName": "MODIFY",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": "2026-02-17T12:55:17-05:00",
"Keys": {
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
}
},
"NewImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "BbBbBbBbBbBbBbBbBbBbBg=="
},
"TTL": {
"N": "123456789"
}
},
"OldImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "IiIiIiIiIiIiIiIiIiIiIg=="
}
},
"SizeBytes": 148,
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
}

View File

@ -0,0 +1,46 @@
{
"Records": [
{
"eventID": "88888888-85b6-4a4b-a74e-bf4688888888",
"eventName": "MODIFY",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": "2026-02-17T12:55:17-05:00",
"Keys": {
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
}
},
"NewImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "BbBbBbBbBbBbBbBbBbBbBg=="
}
},
"OldImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "IiIiIiIiIiIiIiIiIiIiIg=="
},
"TTL": {
"N": "123456789"
}
},
"SizeBytes": 148,
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
}

View File

@ -0,0 +1,46 @@
{
"Records": [
{
"eventID": "88888888-85b6-4a4b-a74e-bf4688888888",
"eventName": "MODIFY",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": "2026-02-17T12:55:17-05:00",
"Keys": {
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
}
},
"NewImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "BbBbBbBbBbBbBbBbBbBbBg=="
}
},
"OldImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "BbBbBbBbBbBbBbBbBbBbBg=="
},
"TTL": {
"N": "123456789"
}
},
"SizeBytes": 148,
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
}

View File

@ -0,0 +1,46 @@
{
"Records": [
{
"eventID": "88888888-85b6-4a4b-a74e-bf4688888888",
"eventName": "MODIFY",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": "2026-02-17T12:55:17-05:00",
"Keys": {
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
}
},
"NewImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "BbBbBbBbBbBbBbBbBbBbBg=="
},
"TTL": {
"N": "123456789"
}
},
"OldImage": {
"F": {
"BOOL": true
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
"U": {
"B": "IiIiIiIiIiIiIiIiIiIiIg=="
}
},
"SizeBytes": 148,
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
]
}

View File

@ -14,6 +14,9 @@
}
},
"NewImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},
@ -22,6 +25,9 @@
}
},
"OldImage": {
"F": {
"BOOL": false
},
"N": {
"B": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE="
},