Reject old-format Benin numbers, which are now undeliverable

This commit is contained in:
Chris Eager
2025-01-03 12:17:13 -06:00
committed by Chris Eager
parent f4a243861c
commit 3a4a55c245
7 changed files with 83 additions and 20 deletions

View File

@@ -175,6 +175,7 @@ import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMa
import org.whispersystems.textsecuregcm.mappers.InvalidWebsocketAddressExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.JsonMappingExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.NonNormalizedPhoneNumberExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.ObsoletePhoneNumberFormatExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RegistrationServiceSenderExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.ServerRejectedExceptionMapper;
@@ -1212,6 +1213,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new ServerRejectedExceptionMapper(),
new ImpossiblePhoneNumberExceptionMapper(),
new NonNormalizedPhoneNumberExceptionMapper(),
new ObsoletePhoneNumberFormatExceptionMapper(),
new RegistrationServiceSenderExceptionMapper(),
new SubscriptionExceptionMapper(),
new JsonMappingExceptionMapper()

View File

@@ -95,6 +95,7 @@ import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
import org.whispersystems.textsecuregcm.util.ObsoletePhoneNumberFormatException;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
@@ -173,7 +174,7 @@ public class VerificationController {
description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed",
schema = @Schema(implementation = Integer.class)))
public VerificationSessionResponse createSession(@NotNull @Valid final CreateVerificationSessionRequest request)
throws RateLimitExceededException {
throws RateLimitExceededException, ObsoletePhoneNumberFormatException {
final Pair<String, PushNotification.TokenType> pushTokenAndType = validateAndExtractPushToken(
request.getUpdateVerificationSessionRequest());

View File

@@ -0,0 +1,18 @@
package org.whispersystems.textsecuregcm.mappers;
import io.micrometer.core.instrument.Metrics;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
import org.whispersystems.textsecuregcm.util.ObsoletePhoneNumberFormatException;
public class ObsoletePhoneNumberFormatExceptionMapper implements ExceptionMapper<ObsoletePhoneNumberFormatException> {
private static final String COUNTER_NAME = MetricsUtil.name(ObsoletePhoneNumberFormatExceptionMapper.class, "errors");
@Override
public Response toResponse(final ObsoletePhoneNumberFormatException exception) {
Metrics.counter(COUNTER_NAME, "regionCode", exception.getRegionCode()).increment();
return Response.status(499).build();
}
}

View File

@@ -0,0 +1,15 @@
package org.whispersystems.textsecuregcm.util;
public class ObsoletePhoneNumberFormatException extends Exception {
private final String regionCode;
public ObsoletePhoneNumberFormatException(final String regionCode) {
super("The provided format is obsolete in %s".formatted(regionCode));
this.regionCode = regionCode;
}
public String getRegionCode() {
return regionCode;
}
}

View File

@@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.random.RandomGenerator;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
public class Util {
@@ -125,7 +124,7 @@ public class Util {
try {
final PhoneNumber phoneNumber = PHONE_NUMBER_UTIL.parse(number, null);
// Benin is changing phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX starting on November 30, 2024
// Benin changed phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX on November 30, 2024
if ("BJ".equals(PHONE_NUMBER_UTIL.getRegionCodeForNumber(phoneNumber))) {
final String nationalSignificantNumber = PHONE_NUMBER_UTIL.getNationalSignificantNumber(phoneNumber);
final String alternateE164;
@@ -176,7 +175,7 @@ public class Util {
throw new IllegalArgumentException("Numbers from different countries cannot be equivalent alternate forms");
}
if (regions.contains("BJ")) {
// Benin is changing phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX starting on November 30, 2024
// Benin changed phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX on November 30, 2024
// We prefer the longest form for long-term stability
return e164s.stream().sorted(Comparator.comparingInt(String::length).reversed()).findFirst();
}
@@ -217,7 +216,7 @@ public class Util {
}
/**
* Benin is changing phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX starting on November 30, 2024.
* Benin changed phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX on November 30, 2024
*
* @param phoneNumber the phone number to check.
* @return whether the provided phone number is an old-format Benin phone number
@@ -235,11 +234,9 @@ public class Util {
* @return the canonical phone number if applicable, otherwise the original phone number.
*/
public static Phonenumber.PhoneNumber canonicalizePhoneNumber(final Phonenumber.PhoneNumber phoneNumber)
throws NumberParseException {
throws NumberParseException, ObsoletePhoneNumberFormatException {
if (isOldFormatBeninPhoneNumber(phoneNumber)) {
// Benin changed phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX starting on November 30, 2024.
final String newFormatNumber = "+22901" + PHONE_NUMBER_UTIL.getNationalSignificantNumber(phoneNumber);
return PhoneNumberUtil.getInstance().parse(newFormatNumber, null);
throw new ObsoletePhoneNumberFormatException("bj");
}
return phoneNumber;
}