Update shape of KeyTransparencyMonitorRequest
This commit is contained in:
parent
2c0fc43137
commit
d925e8af9e
|
@ -167,19 +167,19 @@ public class KeyTransparencyController {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final List<MonitorKey> monitorKeys = new ArrayList<>(List.of(
|
final List<MonitorKey> monitorKeys = new ArrayList<>(List.of(
|
||||||
createMonitorKey(getFullSearchKeyByteString(ACI_PREFIX, request.aci().toCompactByteArray()),
|
createMonitorKey(getFullSearchKeyByteString(ACI_PREFIX, request.aci().value().toCompactByteArray()),
|
||||||
request.aciPositions())
|
request.aci().positions())
|
||||||
));
|
));
|
||||||
|
|
||||||
request.usernameHash().ifPresent(usernameHash ->
|
request.usernameHash().ifPresent(usernameHash ->
|
||||||
monitorKeys.add(createMonitorKey(getFullSearchKeyByteString(USERNAME_PREFIX, usernameHash),
|
monitorKeys.add(createMonitorKey(getFullSearchKeyByteString(USERNAME_PREFIX, usernameHash.value()),
|
||||||
request.usernameHashPositions().get()))
|
usernameHash.positions()))
|
||||||
);
|
);
|
||||||
|
|
||||||
request.e164().ifPresent(e164 ->
|
request.e164().ifPresent(e164 ->
|
||||||
monitorKeys.add(
|
monitorKeys.add(
|
||||||
createMonitorKey(getFullSearchKeyByteString(E164_PREFIX, e164.getBytes(StandardCharsets.UTF_8)),
|
createMonitorKey(getFullSearchKeyByteString(E164_PREFIX, e164.value().getBytes(StandardCharsets.UTF_8)),
|
||||||
request.e164Positions().get()))
|
e164.positions()))
|
||||||
);
|
);
|
||||||
|
|
||||||
return new KeyTransparencyMonitorResponse(keyTransparencyServiceClient.monitor(
|
return new KeyTransparencyMonitorResponse(keyTransparencyServiceClient.monitor(
|
||||||
|
|
|
@ -8,58 +8,78 @@ package org.whispersystems.textsecuregcm.entities;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Positive;
|
||||||
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter;
|
import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter;
|
||||||
import org.whispersystems.textsecuregcm.util.ServiceIdentifierAdapter;
|
import org.whispersystems.textsecuregcm.util.ServiceIdentifierAdapter;
|
||||||
|
|
||||||
import javax.validation.constraints.AssertTrue;
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
import javax.validation.constraints.Positive;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public record KeyTransparencyMonitorRequest(
|
public record KeyTransparencyMonitorRequest(
|
||||||
|
|
||||||
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
AciMonitor aci,
|
||||||
@JsonDeserialize(using = ServiceIdentifierAdapter.AciServiceIdentifierDeserializer.class)
|
|
||||||
@Schema(description = "The aci identifier to monitor")
|
|
||||||
AciServiceIdentifier aci,
|
|
||||||
|
|
||||||
@NotEmpty
|
@Valid
|
||||||
@Schema(description = "A list of log tree positions maintained by the client for the aci search key.")
|
@NotNull
|
||||||
List<@Positive Long> aciPositions,
|
Optional<@Valid E164Monitor> e164,
|
||||||
|
|
||||||
@Schema(description = "The e164-formatted phone number to monitor")
|
@Valid
|
||||||
Optional<String> e164,
|
@NotNull
|
||||||
|
Optional<@Valid UsernameHashMonitor> usernameHash,
|
||||||
@Schema(description = "A list of log tree positions maintained by the client for the e164 search key.")
|
|
||||||
Optional<List<@Positive Long>> e164Positions,
|
|
||||||
|
|
||||||
@JsonSerialize(contentUsing = ByteArrayBase64UrlAdapter.Serializing.class)
|
|
||||||
@JsonDeserialize(contentUsing = ByteArrayBase64UrlAdapter.Deserializing.class)
|
|
||||||
@Schema(description = "The username hash to monitor, encoded in url-safe unpadded base64.")
|
|
||||||
Optional<byte[]> usernameHash,
|
|
||||||
|
|
||||||
@Schema(description = "A list of log tree positions maintained by the client for the username hash search key.")
|
|
||||||
Optional<List<@Positive Long>> usernameHashPositions,
|
|
||||||
|
|
||||||
@Schema(description = "The tree head size to prove consistency against.")
|
@Schema(description = "The tree head size to prove consistency against.")
|
||||||
|
@NotNull
|
||||||
Optional<@Positive Long> lastNonDistinguishedTreeHeadSize,
|
Optional<@Positive Long> lastNonDistinguishedTreeHeadSize,
|
||||||
|
|
||||||
@Schema(description = "The distinguished tree head size to prove consistency against.")
|
@Schema(description = "The distinguished tree head size to prove consistency against.")
|
||||||
|
@NotNull
|
||||||
Optional<@Positive Long> lastDistinguishedTreeHeadSize
|
Optional<@Positive Long> lastDistinguishedTreeHeadSize
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@AssertTrue
|
public record AciMonitor(
|
||||||
public boolean isUsernameHashFieldsValid() {
|
@NotNull
|
||||||
return (usernameHash.isEmpty() && usernameHashPositions.isEmpty()) ||
|
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
||||||
(usernameHash.isPresent() && usernameHashPositions.isPresent() && !usernameHashPositions.get().isEmpty());
|
@JsonDeserialize(using = ServiceIdentifierAdapter.AciServiceIdentifierDeserializer.class)
|
||||||
}
|
@Schema(description = "The aci identifier to monitor")
|
||||||
|
AciServiceIdentifier value,
|
||||||
|
|
||||||
@AssertTrue
|
@Schema(description = "A list of log tree positions maintained by the client for the aci search key.")
|
||||||
public boolean isE164VFieldsValid() {
|
@Valid
|
||||||
return (e164.isEmpty() && e164Positions.isEmpty()) ||
|
@NotNull
|
||||||
(e164.isPresent() && e164Positions.isPresent() && !e164Positions.get().isEmpty());
|
@NotEmpty
|
||||||
}
|
List<@Positive Long> positions
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public record E164Monitor(
|
||||||
|
@Schema(description = "The e164-formatted phone number to monitor")
|
||||||
|
@NotBlank
|
||||||
|
String value,
|
||||||
|
|
||||||
|
@Schema(description = "A list of log tree positions maintained by the client for the e164 search key.")
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty
|
||||||
|
@Valid
|
||||||
|
List<@Positive Long> positions
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public record UsernameHashMonitor(
|
||||||
|
|
||||||
|
@Schema(description = "The username hash to monitor, encoded in url-safe unpadded base64.")
|
||||||
|
@JsonSerialize(using = ByteArrayBase64UrlAdapter.Serializing.class)
|
||||||
|
@JsonDeserialize(using = ByteArrayBase64UrlAdapter.Deserializing.class)
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty
|
||||||
|
byte[] value,
|
||||||
|
|
||||||
|
@Schema(description = "A list of log tree positions maintained by the client for the username hash search key.")
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty
|
||||||
|
@Valid List<@Positive Long> positions
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
@ -129,7 +130,7 @@ public class KeyTransparencyControllerTest {
|
||||||
System.arraycopy(charBytes, 0, expectedFullSearchKey, 0, charBytes.length);
|
System.arraycopy(charBytes, 0, expectedFullSearchKey, 0, charBytes.length);
|
||||||
System.arraycopy(aci, 0, expectedFullSearchKey, charBytes.length, aci.length);
|
System.arraycopy(aci, 0, expectedFullSearchKey, charBytes.length, aci.length);
|
||||||
|
|
||||||
assertArrayEquals(expectedFullSearchKey, getFullSearchKeyByteString(KeyTransparencyController.ACI_PREFIX, aci).toByteArray());
|
assertArrayEquals(expectedFullSearchKey, getFullSearchKeyByteString(ACI_PREFIX, aci).toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
@ -527,8 +528,20 @@ public class KeyTransparencyControllerTest {
|
||||||
final Optional<List<Long>> e164Positions,
|
final Optional<List<Long>> e164Positions,
|
||||||
final Optional<Long> lastTreeHeadSize,
|
final Optional<Long> lastTreeHeadSize,
|
||||||
final Optional<Long> distinguishedTreeHeadSize) {
|
final Optional<Long> distinguishedTreeHeadSize) {
|
||||||
final KeyTransparencyMonitorRequest request = new KeyTransparencyMonitorRequest(aci, aciPositions,
|
|
||||||
e164, e164Positions, usernameHash, usernameHashPositions, lastTreeHeadSize, distinguishedTreeHeadSize);
|
final Optional<KeyTransparencyMonitorRequest.E164Monitor> e164Monitor = e164.map(
|
||||||
|
value -> new KeyTransparencyMonitorRequest.E164Monitor(value, e164Positions.orElse(Collections.emptyList())))
|
||||||
|
.or(() -> e164Positions.map(positions -> new KeyTransparencyMonitorRequest.E164Monitor(null, positions)));
|
||||||
|
|
||||||
|
final Optional<KeyTransparencyMonitorRequest.UsernameHashMonitor> usernameHashMonitor = usernameHash.map(
|
||||||
|
value -> new KeyTransparencyMonitorRequest.UsernameHashMonitor(value,
|
||||||
|
usernameHashPositions.orElse(Collections.emptyList())))
|
||||||
|
.or(() -> usernameHashPositions.map(
|
||||||
|
positions -> new KeyTransparencyMonitorRequest.UsernameHashMonitor(null, positions)));
|
||||||
|
|
||||||
|
final KeyTransparencyMonitorRequest request = new KeyTransparencyMonitorRequest(
|
||||||
|
new KeyTransparencyMonitorRequest.AciMonitor(aci, aciPositions), e164Monitor, usernameHashMonitor,
|
||||||
|
lastTreeHeadSize, distinguishedTreeHeadSize);
|
||||||
try {
|
try {
|
||||||
return SystemMapper.jsonMapper().writeValueAsString(request);
|
return SystemMapper.jsonMapper().writeValueAsString(request);
|
||||||
} catch (final JsonProcessingException e) {
|
} catch (final JsonProcessingException e) {
|
||||||
|
|
Loading…
Reference in New Issue