From 9ffb588c6a6261a2c607dee1da05b09bef363604 Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Thu, 22 Jan 2026 10:42:18 -0500 Subject: [PATCH] Pass carrier data from lookup services to registration service --- .../textsecuregcm/WhisperServerService.java | 2 +- ...DynamicCarrierDataLookupConfiguration.java | 24 +++++++++++++++++ .../dynamic/DynamicConfiguration.java | 8 ++++++ .../controllers/VerificationController.java | 27 ++++++++++++++++--- .../src/main/proto/RegistrationService.proto | 4 +-- .../VerificationControllerTest.java | 7 ++++- 6 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicCarrierDataLookupConfiguration.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 1fdc0f8e3..01cf06050 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -1118,7 +1118,7 @@ public class WhisperServerService extends Application getExperimentEnrollmentConfiguration( final String experimentName) { return Optional.ofNullable(experiments.get(experimentName)); @@ -121,4 +125,8 @@ public class DynamicConfiguration { public DynamicBackupConfiguration getBackupConfiguration() { return backup; } + + public DynamicCarrierDataLookupConfiguration getCarrierDataLookupConfiguration() { + return carrierDataLookup; + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java index 9462718d8..bc0e83ce6 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java @@ -94,6 +94,9 @@ import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers; import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager; import org.whispersystems.textsecuregcm.storage.VerificationSessionManager; +import org.whispersystems.textsecuregcm.telephony.CarrierData; +import org.whispersystems.textsecuregcm.telephony.CarrierDataException; +import org.whispersystems.textsecuregcm.telephony.CarrierDataProvider; import org.whispersystems.textsecuregcm.util.ExceptionUtils; import org.whispersystems.textsecuregcm.util.ObsoletePhoneNumberFormatException; import org.whispersystems.textsecuregcm.util.Pair; @@ -129,6 +132,7 @@ public class VerificationController { private final PhoneNumberIdentifiers phoneNumberIdentifiers; private final RateLimiters rateLimiters; private final AccountsManager accountsManager; + private final CarrierDataProvider carrierDataProvider; private final RegistrationFraudChecker registrationFraudChecker; private final DynamicConfigurationManager dynamicConfigurationManager; private final Clock clock; @@ -141,6 +145,7 @@ public class VerificationController { final PhoneNumberIdentifiers phoneNumberIdentifiers, final RateLimiters rateLimiters, final AccountsManager accountsManager, + final CarrierDataProvider carrierDataProvider, final RegistrationFraudChecker registrationFraudChecker, final DynamicConfigurationManager dynamicConfigurationManager, final Clock clock) { @@ -152,6 +157,7 @@ public class VerificationController { this.phoneNumberIdentifiers = phoneNumberIdentifiers; this.rateLimiters = rateLimiters; this.accountsManager = accountsManager; + this.carrierDataProvider = carrierDataProvider; this.registrationFraudChecker = registrationFraudChecker; this.dynamicConfigurationManager = dynamicConfigurationManager; this.clock = clock; @@ -187,14 +193,29 @@ public class VerificationController { throw new ServerErrorException("could not parse already validated number", Response.Status.INTERNAL_SERVER_ERROR); } + Optional maybeCarrierData; + + if (dynamicConfigurationManager.getConfiguration().getCarrierDataLookupConfiguration().enabled()) { + try { + maybeCarrierData = carrierDataProvider.lookupCarrierData(phoneNumber, + dynamicConfigurationManager.getConfiguration().getCarrierDataLookupConfiguration().maxCacheAge()); + } catch (final IOException | CarrierDataException e) { + logger.warn("Failed to retrieve carrier data", e); + maybeCarrierData = Optional.empty(); + } + } else { + maybeCarrierData = Optional.empty(); + } + final RegistrationServiceSession registrationServiceSession; try { final String sourceHost = (String) requestContext.getProperty(RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME); - registrationServiceSession = registrationServiceClient.createRegistrationSession(phoneNumber, sourceHost, + registrationServiceSession = registrationServiceClient.createRegistrationSession(phoneNumber, + sourceHost, accountsManager.getByE164(request.number()).isPresent(), - request.updateVerificationSessionRequest().mcc(), - request.updateVerificationSessionRequest().mnc(), + maybeCarrierData.flatMap(CarrierData::mcc).orElse(null), + maybeCarrierData.flatMap(CarrierData::mnc).orElse(null), REGISTRATION_RPC_TIMEOUT).join(); } catch (final CancellationException e) { diff --git a/service/src/main/proto/RegistrationService.proto b/service/src/main/proto/RegistrationService.proto index a3ad8935a..e1f9bc1f3 100644 --- a/service/src/main/proto/RegistrationService.proto +++ b/service/src/main/proto/RegistrationService.proto @@ -47,12 +47,12 @@ message CreateRegistrationSessionRequest { string rate_limit_collation_key = 3; /** - * The MCC for the given `e164` as reported by the client. + * The MCC for the given `e164` as reported by a number lookup service. */ string mcc = 4; /** - * The MNC for the given `e164` as reported by the client. + * The MNC for the given `e164` as reported by a number lookup service. */ string mnc = 5; } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java index 47f6b1393..546133ec5 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java @@ -58,6 +58,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.whispersystems.textsecuregcm.captcha.AssessmentResult; import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicCarrierDataLookupConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRegistrationConfiguration; import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession; @@ -83,6 +84,7 @@ import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers; import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager; import org.whispersystems.textsecuregcm.storage.VerificationSessionManager; +import org.whispersystems.textsecuregcm.telephony.CarrierDataProvider; import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.TestRemoteAddressFilterProvider; @@ -106,6 +108,7 @@ class VerificationControllerTest { private final PhoneNumberIdentifiers phoneNumberIdentifiers = mock(PhoneNumberIdentifiers.class); private final RateLimiters rateLimiters = mock(RateLimiters.class); private final AccountsManager accountsManager = mock(AccountsManager.class); + private final CarrierDataProvider carrierDataProvider = mock(CarrierDataProvider.class); private final Clock clock = Clock.systemUTC(); private final RateLimiter captchaLimiter = mock(RateLimiter.class); @@ -127,7 +130,7 @@ class VerificationControllerTest { .addResource( new VerificationController(registrationServiceClient, verificationSessionManager, pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, phoneNumberIdentifiers, rateLimiters, accountsManager, - RegistrationFraudChecker.noop(), dynamicConfigurationManager, clock)) + carrierDataProvider, RegistrationFraudChecker.noop(), dynamicConfigurationManager, clock)) .build(); @BeforeEach @@ -140,6 +143,8 @@ class VerificationControllerTest { .thenReturn(Optional.empty()); when(dynamicConfiguration.getRegistrationConfiguration()) .thenReturn(new DynamicRegistrationConfiguration(false)); + when(dynamicConfiguration.getCarrierDataLookupConfiguration()) + .thenReturn(new DynamicCarrierDataLookupConfiguration()); when(dynamicConfigurationManager.getConfiguration()) .thenReturn(dynamicConfiguration); when(phoneNumberIdentifiers.getPhoneNumberIdentifier(NUMBER))