5353import java .util .function .Function ;
5454import java .util .stream .Collectors ;
5555import javax .annotation .Nullable ;
56- import org .apache .commons .lang3 .StringUtils ;
5756import org .glassfish .jersey .server .ManagedAsync ;
5857import org .signal .libsignal .protocol .IdentityKey ;
5958import org .signal .libsignal .protocol .ServiceId ;
8079import org .whispersystems .textsecuregcm .entities .ExpiringProfileKeyCredentialProfileResponse ;
8180import org .whispersystems .textsecuregcm .entities .ProfileAvatarUploadAttributes ;
8281import org .whispersystems .textsecuregcm .entities .VersionedProfileResponse ;
83- import org .whispersystems .textsecuregcm .filters .RemoteAddressFilter ;
8482import org .whispersystems .textsecuregcm .identity .AciServiceIdentifier ;
8583import org .whispersystems .textsecuregcm .identity .IdentityType ;
8684import org .whispersystems .textsecuregcm .identity .PniServiceIdentifier ;
8785import org .whispersystems .textsecuregcm .identity .ServiceIdentifier ;
86+ import org .whispersystems .textsecuregcm .limits .RateLimitedByIp ;
8887import org .whispersystems .textsecuregcm .limits .RateLimiters ;
8988import org .whispersystems .textsecuregcm .metrics .UserAgentTagUtil ;
9089import org .whispersystems .textsecuregcm .s3 .PolicySigner ;
9695import org .whispersystems .textsecuregcm .storage .DynamicConfigurationManager ;
9796import org .whispersystems .textsecuregcm .storage .ProfilesManager ;
9897import org .whispersystems .textsecuregcm .storage .VersionedProfile ;
99- import org .whispersystems .textsecuregcm .util .ExceptionUtils ;
10098import org .whispersystems .textsecuregcm .util .HeaderUtils ;
10199import org .whispersystems .textsecuregcm .util .Pair ;
102100import 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