Pass carrier data from lookup services to registration service

This commit is contained in:
Jon Chambers
2026-01-22 10:42:18 -05:00
committed by Jon Chambers
parent 5043175cb4
commit 9ffb588c6a
6 changed files with 65 additions and 7 deletions

View File

@@ -1118,7 +1118,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getCdnConfiguration().bucket()),
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager,
phoneNumberIdentifiers, rateLimiters, accountsManager, registrationFraudChecker,
phoneNumberIdentifiers, rateLimiters, accountsManager, carrierDataProvider, registrationFraudChecker,
dynamicConfigurationManager, clock)
);
if (config.getSubscription() != null && config.getOneTimeDonations() != null) {

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration.dynamic;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
public record DynamicCarrierDataLookupConfiguration(boolean enabled, @NotNull Duration maxCacheAge) {
public static Duration DEFAULT_MAX_CACHE_AGE = Duration.ofDays(7);
public DynamicCarrierDataLookupConfiguration() {
this(false, DEFAULT_MAX_CACHE_AGE);
}
public DynamicCarrierDataLookupConfiguration {
if (maxCacheAge == null) {
maxCacheAge = DEFAULT_MAX_CACHE_AGE;
}
}
}

View File

@@ -68,6 +68,10 @@ public class DynamicConfiguration {
@Valid
private DynamicBackupConfiguration backup = new DynamicBackupConfiguration();
@JsonProperty
@Valid
private DynamicCarrierDataLookupConfiguration carrierDataLookup = new DynamicCarrierDataLookupConfiguration();
public Optional<DynamicExperimentEnrollmentConfiguration> 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;
}
}

View File

@@ -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<DynamicConfiguration> 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<DynamicConfiguration> 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<CarrierData> 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) {