From 1b09529ecefc7a69127d5f624458259ac53917fa Mon Sep 17 00:00:00 2001 From: Katherine Date: Wed, 24 Jun 2026 15:02:34 -0700 Subject: [PATCH] Use V2 key transparency query RPCs --- .../KeyTransparencyController.java | 43 ++++++--- .../grpc/KeyTransparencyGrpcService.java | 87 ++++--------------- .../KeyTransparencyServiceClient.java | 17 ++-- .../main/proto/KeyTransparencyService.proto | 48 ++++++---- .../KeyTransparencyControllerTest.java | 13 ++- .../grpc/KeyTransparencyGrpcServiceTest.java | 32 +++---- 6 files changed, 118 insertions(+), 122 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyController.java index 49ad06980..f3c25ac13 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyController.java @@ -33,6 +33,8 @@ import org.glassfish.jersey.server.ManagedAsync; import org.signal.keytransparency.client.AciMonitorRequest; import org.signal.keytransparency.client.E164MonitorRequest; import org.signal.keytransparency.client.E164SearchRequest; +import org.signal.keytransparency.client.MonitorResponseV2; +import org.signal.keytransparency.client.SearchResponseV2; import org.signal.keytransparency.client.UsernameHashMonitorRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,15 +103,23 @@ public class KeyTransparencyController { .build() )); - return new KeyTransparencySearchResponse( - keyTransparencyServiceClient.search( - ByteString.copyFrom(request.aci().toCompactByteArray()), - ByteString.copyFrom(request.aciIdentityKey().serialize()), - request.usernameHash().map(ByteString::copyFrom), - maybeE164SearchRequest, - request.lastTreeHeadSize(), - request.distinguishedTreeHeadSize()) - .toByteArray()); + final SearchResponseV2 searchResponse = keyTransparencyServiceClient.search( + ByteString.copyFrom(request.aci().toCompactByteArray()), + ByteString.copyFrom(request.aciIdentityKey().serialize()), + request.usernameHash().map(ByteString::copyFrom), + maybeE164SearchRequest, + request.lastTreeHeadSize(), + request.distinguishedTreeHeadSize()); + + if (searchResponse.hasPermissionDenied()) { + throw new StatusRuntimeException(Status.PERMISSION_DENIED); + } + + if (!searchResponse.hasSearchResponse()) { + throw new StatusRuntimeException(Status.UNAVAILABLE.withDescription("Missing search response")); + } + + return new KeyTransparencySearchResponse(searchResponse.getSearchResponse().toByteArray()); } catch (final StatusRuntimeException exception) { handleKeyTransparencyServiceError(exception); } @@ -163,13 +173,22 @@ public class KeyTransparencyController { .setCommitmentIndex(ByteString.copyFrom(e164.commitmentIndex())) .build()); - return new KeyTransparencyMonitorResponse(keyTransparencyServiceClient.monitor( + final MonitorResponseV2 monitorResponse = keyTransparencyServiceClient.monitor( aciMonitorRequest, usernameHashMonitorRequest, e164MonitorRequest, request.lastNonDistinguishedTreeHeadSize(), - request.lastDistinguishedTreeHeadSize()) - .toByteArray()); + request.lastDistinguishedTreeHeadSize()); + + if (monitorResponse.hasPermissionDenied()) { + throw new StatusRuntimeException(Status.PERMISSION_DENIED); + } + + if (!monitorResponse.hasMonitorResponse()) { + throw new StatusRuntimeException(Status.UNAVAILABLE.withDescription("Missing monitor response")); + } + + return new KeyTransparencyMonitorResponse(monitorResponse.getMonitorResponse().toByteArray()); } catch (final StatusRuntimeException exception) { handleKeyTransparencyServiceError(exception); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcService.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcService.java index 5c1bf7c69..c3bdb4fe6 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcService.java @@ -15,8 +15,10 @@ import org.signal.keytransparency.client.E164MonitorRequest; import org.signal.keytransparency.client.E164SearchRequest; import org.signal.keytransparency.client.MonitorRequest; import org.signal.keytransparency.client.MonitorResponse; +import org.signal.keytransparency.client.MonitorResponseV2; import org.signal.keytransparency.client.SearchRequest; import org.signal.keytransparency.client.SearchResponse; +import org.signal.keytransparency.client.SearchResponseV2; import org.signal.keytransparency.client.SimpleKeyTransparencyQueryServiceGrpc; import org.signal.keytransparency.client.UsernameHashMonitorRequest; import org.whispersystems.textsecuregcm.controllers.AccountController; @@ -39,102 +41,51 @@ public class KeyTransparencyGrpcService extends } @Override - public SearchResponse search(final SearchRequest request) throws RateLimitExceededException { + public SearchResponseV2 searchV2(final SearchRequest request) throws RateLimitExceededException { rateLimiters.getKeyTransparencySearchLimiter().validate(RequestAttributesUtil.getRemoteAddress().getHostAddress()); return client.search(validateSearchRequest(request)); } @Override - public MonitorResponse monitor(final MonitorRequest request) throws RateLimitExceededException { + public MonitorResponseV2 monitorV2(final MonitorRequest request) throws RateLimitExceededException { rateLimiters.getKeyTransparencyMonitorLimiter().validate(RequestAttributesUtil.getRemoteAddress().getHostAddress()); return client.monitor(validateMonitorRequest(request)); } @Override - public DistinguishedResponse distinguished(final DistinguishedRequest request) throws RateLimitExceededException { + public DistinguishedResponse distinguishedV2(final DistinguishedRequest request) throws RateLimitExceededException { rateLimiters.getKeyTransparencyDistinguishedLimiter().validate(RequestAttributesUtil.getRemoteAddress().getHostAddress()); - // A client's very first distinguished request will not have a "last" parameter - if (request.hasLast() && request.getLast() <= 0) { - throw Status.INVALID_ARGUMENT.withDescription("Last tree head size must be positive").asRuntimeException(); - } return client.distinguished(request); } private SearchRequest validateSearchRequest(final SearchRequest request) { + validateAci(request.getAci().toByteArray()); + if (request.hasE164SearchRequest()) { final E164SearchRequest e164SearchRequest = request.getE164SearchRequest(); if (e164SearchRequest.getUnidentifiedAccessKey().isEmpty() != e164SearchRequest.getE164().isEmpty()) { - throw Status.INVALID_ARGUMENT.withDescription("Unidentified access key and E164 must be provided together or not at all").asRuntimeException(); + throw GrpcExceptions.fieldViolation("e164_search_request", "Unidentified access key and E164 must be provided together or not at all"); } } - if (!request.getConsistency().hasDistinguished()) { - throw Status.INVALID_ARGUMENT.withDescription("Must provide distinguished tree head size").asRuntimeException(); - } - - validateConsistencyParameters(request.getConsistency()); return request; } + private void validateAci(final byte[] aci) { + try { + AciServiceIdentifier.fromBytes(aci); + } catch (IllegalArgumentException e) { + throw GrpcExceptions.fieldViolation("aci", "Invalid ACI"); + } + } + private MonitorRequest validateMonitorRequest(final MonitorRequest request) { - final AciMonitorRequest aciMonitorRequest = request.getAci(); + validateAci(request.getAci().getAci().toByteArray()); - try { - AciServiceIdentifier.fromBytes(aciMonitorRequest.getAci().toByteArray()); - } catch (IllegalArgumentException e) { - throw Status.INVALID_ARGUMENT.withDescription("Invalid ACI").asRuntimeException(); - } - if (aciMonitorRequest.getEntryPosition() <= 0) { - throw Status.INVALID_ARGUMENT.withDescription("Aci entry position must be positive").asRuntimeException(); - } - if (aciMonitorRequest.getCommitmentIndex().size() != COMMITMENT_INDEX_LENGTH) { - throw Status.INVALID_ARGUMENT.withDescription("Aci commitment index must be 32 bytes").asRuntimeException(); + if (!request.getConsistency().hasLast()) { + throw GrpcExceptions.fieldViolation("consistency_last", "Must provide distinguished and last tree head sizes"); } - if (request.hasUsernameHash()) { - final UsernameHashMonitorRequest usernameHashMonitorRequest = request.getUsernameHash(); - if (usernameHashMonitorRequest.getUsernameHash().isEmpty()) { - throw Status.INVALID_ARGUMENT.withDescription("Username hash cannot be empty").asRuntimeException(); - } - if (usernameHashMonitorRequest.getUsernameHash().size() != AccountController.USERNAME_HASH_LENGTH) { - throw Status.INVALID_ARGUMENT.withDescription("Invalid username hash length").asRuntimeException(); - } - if (usernameHashMonitorRequest.getEntryPosition() <= 0) { - throw Status.INVALID_ARGUMENT.withDescription("Username hash entry position must be positive").asRuntimeException(); - } - if (usernameHashMonitorRequest.getCommitmentIndex().size() != COMMITMENT_INDEX_LENGTH) { - throw Status.INVALID_ARGUMENT.withDescription("Username hash commitment index must be 32 bytes").asRuntimeException(); - } - } - - if (request.hasE164()) { - final E164MonitorRequest e164MonitorRequest = request.getE164(); - if (e164MonitorRequest.getE164().isEmpty()) { - throw Status.INVALID_ARGUMENT.withDescription("E164 cannot be empty").asRuntimeException(); - } - if (e164MonitorRequest.getEntryPosition() <= 0) { - throw Status.INVALID_ARGUMENT.withDescription("E164 entry position must be positive").asRuntimeException(); - } - if (e164MonitorRequest.getCommitmentIndex().size() != COMMITMENT_INDEX_LENGTH) { - throw Status.INVALID_ARGUMENT.withDescription("E164 commitment index must be 32 bytes").asRuntimeException(); - } - } - - if (!request.getConsistency().hasDistinguished() || !request.getConsistency().hasLast()) { - throw Status.INVALID_ARGUMENT.withDescription("Must provide distinguished and last tree head sizes").asRuntimeException(); - } - - validateConsistencyParameters(request.getConsistency()); return request; } - - private static void validateConsistencyParameters(final ConsistencyParameters consistency) { - if (consistency.getDistinguished() <= 0) { - throw Status.INVALID_ARGUMENT.withDescription("Distinguished tree head size must be positive").asRuntimeException(); - } - - if (consistency.hasLast() && consistency.getLast() <= 0) { - throw Status.INVALID_ARGUMENT.withDescription("Last tree head size must be positive").asRuntimeException(); - } - } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/keytransparency/KeyTransparencyServiceClient.java b/service/src/main/java/org/whispersystems/textsecuregcm/keytransparency/KeyTransparencyServiceClient.java index e455fb4ae..6c0638ea1 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/keytransparency/KeyTransparencyServiceClient.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/keytransparency/KeyTransparencyServiceClient.java @@ -29,8 +29,10 @@ import org.signal.keytransparency.client.E164SearchRequest; import org.signal.keytransparency.client.KeyTransparencyQueryServiceGrpc; import org.signal.keytransparency.client.MonitorRequest; import org.signal.keytransparency.client.MonitorResponse; +import org.signal.keytransparency.client.MonitorResponseV2; import org.signal.keytransparency.client.SearchRequest; import org.signal.keytransparency.client.SearchResponse; +import org.signal.keytransparency.client.SearchResponseV2; import org.signal.keytransparency.client.UsernameHashMonitorRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,7 +112,7 @@ public class KeyTransparencyServiceClient implements Managed { } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public SearchResponse search( + public SearchResponseV2 search( final ByteString aci, final ByteString aciIdentityKey, final Optional usernameHash, @@ -132,13 +134,13 @@ public class KeyTransparencyServiceClient implements Managed { return search(searchRequestBuilder.build()); } - public SearchResponse search(final SearchRequest request) { + public SearchResponseV2 search(final SearchRequest request) { return stub.withDeadline(toDeadline(KEY_TRANSPARENCY_RPC_TIMEOUT)) - .search(request); + .searchV2(request); } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public MonitorResponse monitor(final AciMonitorRequest aciMonitorRequest, + public MonitorResponseV2 monitor(final AciMonitorRequest aciMonitorRequest, final Optional usernameHashMonitorRequest, final Optional e164MonitorRequest, final long lastTreeHeadSize, @@ -155,12 +157,11 @@ public class KeyTransparencyServiceClient implements Managed { return monitor(monitorRequestBuilder.build()); } - public MonitorResponse monitor(final MonitorRequest request) { + public MonitorResponseV2 monitor(final MonitorRequest request) { return stub.withDeadline(toDeadline(KEY_TRANSPARENCY_RPC_TIMEOUT)) - .monitor(request); + .monitorV2(request); } - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public DistinguishedResponse getDistinguishedKey(final Optional lastTreeHeadSize) { final DistinguishedRequest request = lastTreeHeadSize.map( @@ -171,7 +172,7 @@ public class KeyTransparencyServiceClient implements Managed { public DistinguishedResponse distinguished(final DistinguishedRequest request) { return stub.withDeadline(toDeadline(KEY_TRANSPARENCY_RPC_TIMEOUT)) - .distinguished(request); + .distinguishedV2(request); } private static Deadline toDeadline(final Duration timeout) { diff --git a/service/src/main/proto/KeyTransparencyService.proto b/service/src/main/proto/KeyTransparencyService.proto index ad0a46194..402dcdde5 100644 --- a/service/src/main/proto/KeyTransparencyService.proto +++ b/service/src/main/proto/KeyTransparencyService.proto @@ -34,17 +34,17 @@ service KeyTransparencyQueryService { * subsequent Search and Monitor requests. It should be the first key * transparency RPC a client calls. */ - rpc Distinguished(DistinguishedRequest) returns (DistinguishedResponse) {} + rpc DistinguishedV2(DistinguishedRequest) returns (DistinguishedResponse) {} /** * An endpoint used by clients to search for one or more identifiers in the transparency log. * The server returns proof that the identifier(s) exist in the log. */ - rpc Search(SearchRequest) returns (SearchResponse) {} + rpc SearchV2(SearchRequest) returns (SearchResponseV2) {} /** * An endpoint that allows users to monitor a group of identifiers by returning proof that the log continues to be * constructed correctly in later entries for those identifiers. */ - rpc Monitor(MonitorRequest) returns (MonitorResponse) {} + rpc MonitorV2(MonitorRequest) returns (MonitorResponseV2) {} } message SearchRequest { @@ -112,6 +112,19 @@ message SearchResponse { optional CondensedTreeSearchResponse username_hash = 4; } +/** + * Indicates that the client is not authorized to make the request because + * it did not provide the correct set of data. + */ +message PermissionDenied {} + +message SearchResponseV2 { + oneof response { + SearchResponse search_response = 1; + PermissionDenied permission_denied = 2; + } +} + /** * The tree head size(s) to prove consistency against. A client's very first * key transparency request should be looking up the "distinguished" key; @@ -124,13 +137,11 @@ message ConsistencyParameters { * This field may be omitted if the client is looking up an identifier * for the first time. */ - optional uint64 last = 1; + optional uint64 last = 1 [(org.signal.chat.require.range) = {min: 1}]; /** * The distinguished tree head size to prove consistency against. - * This field may be omitted when the client is looking up the - * "distinguished" key for the very first time. */ - optional uint64 distinguished = 2; + uint64 distinguished = 2 [(org.signal.chat.require.range) = {min: 1}]; } /** @@ -143,7 +154,7 @@ message DistinguishedRequest { * exception of a client's very first request, this field should always be * set. */ - optional uint64 last = 1; + optional uint64 last = 1 [(org.signal.chat.require.range) = {min: 1}]; } /** @@ -343,20 +354,20 @@ message MonitorRequest { message AciMonitorRequest { bytes aci = 1 [(org.signal.chat.require.exactlySize) = 16]; - uint64 entry_position = 2; + uint64 entry_position = 2 [(org.signal.chat.require.range) = {min: 1}]; bytes commitment_index = 3 [(org.signal.chat.require.exactlySize) = 32]; } message UsernameHashMonitorRequest { - bytes username_hash = 1 [(org.signal.chat.require.exactlySize) = 0, (org.signal.chat.require.exactlySize) = 32]; - uint64 entry_position = 2; - bytes commitment_index = 3 [(org.signal.chat.require.exactlySize) = 0, (org.signal.chat.require.exactlySize) = 32]; + bytes username_hash = 1 [(org.signal.chat.require.exactlySize) = 32]; + uint64 entry_position = 2 [(org.signal.chat.require.range) = {min: 1}]; + bytes commitment_index = 3 [(org.signal.chat.require.exactlySize) = 32]; } message E164MonitorRequest { - optional string e164 = 1 [(org.signal.chat.require.e164) = true]; - uint64 entry_position = 2; - bytes commitment_index = 3 [(org.signal.chat.require.exactlySize) = 0, (org.signal.chat.require.exactlySize) = 32]; + string e164 = 1 [(org.signal.chat.require.e164) = true]; + uint64 entry_position = 2 [(org.signal.chat.require.range) = {min: 1}]; + bytes commitment_index = 3 [(org.signal.chat.require.exactlySize) = 32]; } message MonitorProof { @@ -397,3 +408,10 @@ message MonitorResponse { */ repeated bytes inclusion = 5; } + +message MonitorResponseV2 { + oneof response { + MonitorResponse monitor_response = 1; + PermissionDenied permission_denied = 2; + } +} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyControllerTest.java index b6e36a2e8..112e3f36d 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/KeyTransparencyControllerTest.java @@ -54,8 +54,10 @@ import org.signal.keytransparency.client.DistinguishedResponse; import org.signal.keytransparency.client.E164SearchRequest; import org.signal.keytransparency.client.FullTreeHead; import org.signal.keytransparency.client.MonitorResponse; +import org.signal.keytransparency.client.MonitorResponseV2; import org.signal.keytransparency.client.SearchProof; import org.signal.keytransparency.client.SearchResponse; +import org.signal.keytransparency.client.SearchResponseV2; import org.signal.keytransparency.client.UpdateValue; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.ecc.ECKeyPair; @@ -143,7 +145,10 @@ public class KeyTransparencyControllerTest { usernameHash.ifPresent(ignored -> searchResponseBuilder.setUsernameHash(CondensedTreeSearchResponse.getDefaultInstance())); when(keyTransparencyServiceClient.search(any(), any(), any(), any(), any(), anyLong())) - .thenReturn(searchResponseBuilder.build()); + .thenReturn(SearchResponseV2.newBuilder() + .setSearchResponse(searchResponseBuilder.build()) + .build() + ); final Invocation.Builder request = resources.getJerseyTest() .target("/v1/key-transparency/search") @@ -291,7 +296,9 @@ public class KeyTransparencyControllerTest { @Test void monitorSuccess() { when(keyTransparencyServiceClient.monitor(any(), any(), any(), anyLong(), anyLong())) - .thenReturn(MonitorResponse.getDefaultInstance()); + .thenReturn(MonitorResponseV2.newBuilder() + .setMonitorResponse(MonitorResponse.getDefaultInstance()) + .build()); final Invocation.Builder request = resources.getJerseyTest() .target("/v1/key-transparency/monitor") @@ -306,7 +313,7 @@ public class KeyTransparencyControllerTest { final KeyTransparencyMonitorResponse keyTransparencyMonitorResponse = response.readEntity( KeyTransparencyMonitorResponse.class); - assertNotNull(keyTransparencyMonitorResponse.serializedResponse()); + assertArrayEquals(MonitorResponse.getDefaultInstance().toByteArray(), keyTransparencyMonitorResponse.serializedResponse()); verify(keyTransparencyServiceClient, times(1)).monitor( any(), any(), any(), eq(3L), eq(4L)); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcServiceTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcServiceTest.java index 3ce374541..1a56cfbbd 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcServiceTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/KeyTransparencyGrpcServiceTest.java @@ -23,9 +23,9 @@ import org.signal.keytransparency.client.E164MonitorRequest; import org.signal.keytransparency.client.E164SearchRequest; import org.signal.keytransparency.client.KeyTransparencyQueryServiceGrpc; import org.signal.keytransparency.client.MonitorRequest; -import org.signal.keytransparency.client.MonitorResponse; +import org.signal.keytransparency.client.MonitorResponseV2; import org.signal.keytransparency.client.SearchRequest; -import org.signal.keytransparency.client.SearchResponse; +import org.signal.keytransparency.client.SearchResponseV2; import org.signal.keytransparency.client.UsernameHashMonitorRequest; import org.signal.libsignal.protocol.IdentityKey; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; @@ -56,7 +56,7 @@ import static org.whispersystems.textsecuregcm.grpc.GrpcTestUtils.assertRateLimi import static org.whispersystems.textsecuregcm.grpc.GrpcTestUtils.assertStatusException; import static org.whispersystems.textsecuregcm.grpc.KeyTransparencyGrpcService.COMMITMENT_INDEX_LENGTH; -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "ThrowableNotThrown", "ResultOfMethodCallIgnored"}) public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest{ @Mock private KeyTransparencyServiceClient keyTransparencyServiceClient; @@ -80,7 +80,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().search(request)); + assertDoesNotThrow(() -> unauthenticatedServiceStub().searchV2(request)); verify(keyTransparencyServiceClient, times(1)).search(eq(request)); } @@ -121,7 +121,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().search(requestBuilder.build())); + assertStatusException(Status.INVALID_ARGUMENT, () -> unauthenticatedServiceStub().searchV2(requestBuilder.build())); verifyNoInteractions(keyTransparencyServiceClient); } @@ -152,13 +152,13 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().search(request)); + assertRateLimitExceeded(retryAfterDuration, () -> unauthenticatedServiceStub().searchV2(request)); verifyNoInteractions(keyTransparencyServiceClient); } @Test void monitorSuccess() { - when(keyTransparencyServiceClient.monitor(any())).thenReturn(MonitorResponse.getDefaultInstance()); + when(keyTransparencyServiceClient.monitor(any())).thenReturn(MonitorResponseV2.getDefaultInstance()); when(rateLimiter.validateReactive(any(String.class))) .thenReturn(Mono.empty()); final AciMonitorRequest aciMonitorRequest = AciMonitorRequest.newBuilder() @@ -175,7 +175,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().monitor(request)); + assertDoesNotThrow(() -> unauthenticatedServiceStub().monitorV2(request)); verify(keyTransparencyServiceClient, times(1)).monitor(eq(request)); } @@ -199,7 +199,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().monitor(requestBuilder.build())); + assertStatusException(Status.INVALID_ARGUMENT, () -> unauthenticatedServiceStub().monitorV2(requestBuilder.build())); } private static Stream monitorInvalidRequest() { @@ -218,9 +218,9 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().monitor(request)); + assertRateLimitExceeded(retryAfterDuration, () -> unauthenticatedServiceStub().monitorV2(request)); verifyNoInteractions(keyTransparencyServiceClient); } @@ -254,7 +254,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().distinguished(request)); + assertDoesNotThrow(() -> unauthenticatedServiceStub().distinguishedV2(request)); verify(keyTransparencyServiceClient, times(1)).distinguished(eq(request)); } @@ -264,7 +264,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().distinguished(request)); + assertStatusException(Status.INVALID_ARGUMENT, () -> unauthenticatedServiceStub().distinguishedV2(request)); verifyNoInteractions(keyTransparencyServiceClient); } @@ -277,7 +277,7 @@ public class KeyTransparencyGrpcServiceTest extends SimpleBaseGrpcTest unauthenticatedServiceStub().distinguished(request)); + assertRateLimitExceeded(retryAfterDuration, () -> unauthenticatedServiceStub().distinguishedV2(request)); verifyNoInteractions(keyTransparencyServiceClient); }