Skip to content

Commit 52b4e0b

Browse files
ameya-signaljon-signal
authored andcommitted
Enforce rate limiting on batch identity check endpoint
1 parent 3e19b41 commit 52b4e0b

1 file changed

Lines changed: 4 additions & 18 deletions

File tree

service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import java.util.function.Function;
5454
import java.util.stream.Collectors;
5555
import javax.annotation.Nullable;
56-
import org.apache.commons.lang3.StringUtils;
5756
import org.glassfish.jersey.server.ManagedAsync;
5857
import org.signal.libsignal.protocol.IdentityKey;
5958
import org.signal.libsignal.protocol.ServiceId;
@@ -80,11 +79,11 @@
8079
import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialProfileResponse;
8180
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
8281
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
83-
import org.whispersystems.textsecuregcm.filters.RemoteAddressFilter;
8482
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
8583
import org.whispersystems.textsecuregcm.identity.IdentityType;
8684
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
8785
import org.whispersystems.textsecuregcm.identity.ServiceIdentifier;
86+
import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
8887
import org.whispersystems.textsecuregcm.limits.RateLimiters;
8988
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
9089
import org.whispersystems.textsecuregcm.s3.PolicySigner;
@@ -96,7 +95,6 @@
9695
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
9796
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
9897
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
99-
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
10098
import org.whispersystems.textsecuregcm.util.HeaderUtils;
10199
import org.whispersystems.textsecuregcm.util.Pair;
102100
import org.whispersystems.textsecuregcm.util.ProfileHelper;
@@ -125,7 +123,6 @@ public class ProfileController {
125123

126124
private static final String VERSION_NOT_FOUND_COUNTER_NAME = name(ProfileController.class, "versionNotFound");
127125
private static final String DUPLICATE_AUTHENTICATION_COUNTER_NAME = name(ProfileController.class, "duplicateAuthentication");
128-
private static final String BATCH_IDENTITY_CHECK_RATE_LIMITED_COUNTER_NAME = name(ProfileController.class, "batchIdentityCheckRateLimited");
129126

130127
public ProfileController(
131128
Clock clock,
@@ -368,6 +365,7 @@ public BaseProfileResponse getUnversionedProfile(
368365
@Path("/identity_check/batch")
369366
@Consumes(MediaType.APPLICATION_JSON)
370367
@Produces(MediaType.APPLICATION_JSON)
368+
@RateLimitedByIp(RateLimiters.For.BATCH_IDENTITY_CHECK)
371369
@Operation(
372370
summary = "Batch identity key check",
373371
description = "Checks identity key fingerprints for multiple accounts. Returns accounts where the fingerprint does not match. Should not be authenticated.")
@@ -376,20 +374,8 @@ public BaseProfileResponse getUnversionedProfile(
376374
description = "Batch check completed successfully. Response may contain accounts with mismatched fingerprints.",
377375
content = @Content(schema = @Schema(implementation = BatchIdentityCheckResponse.class)))
378376
@ApiResponse(responseCode = "400", description = "Invalid request format or validation failed.")
379-
public CompletableFuture<BatchIdentityCheckResponse> runBatchIdentityCheck(
380-
@NotNull @Valid final BatchIdentityCheckRequest request,
381-
@HeaderParam(HttpHeaders.USER_AGENT) final String userAgent,
382-
@Context final ContainerRequestContext containerRequestContext) {
383-
final String remoteAddress = (String) containerRequestContext.getProperty(RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME);
384-
if (StringUtils.isNotBlank(remoteAddress)) {
385-
rateLimiters.getBatchIdentityCheckLimiter().validateAsync(remoteAddress)
386-
.whenComplete((_, t) -> {
387-
if (t != null && ExceptionUtils.unwrap(t) instanceof RateLimitExceededException) {
388-
Metrics.counter(BATCH_IDENTITY_CHECK_RATE_LIMITED_COUNTER_NAME,
389-
Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))).increment();
390-
}
391-
});
392-
}
377+
@ApiResponse(responseCode = "429", description = "Rate limit exceeded.")
378+
public CompletableFuture<BatchIdentityCheckResponse> runBatchIdentityCheck(@NotNull @Valid final BatchIdentityCheckRequest request) {
393379
return CompletableFuture.supplyAsync(() -> {
394380
List<BatchIdentityCheckResponse.Element> responseElements = Collections.synchronizedList(new ArrayList<>());
395381

0 commit comments

Comments
 (0)