Add a method for verifying that numbers are normalized in addition to being dialable

This commit is contained in:
Jon Chambers 2021-10-20 12:16:17 -04:00 committed by Jon Chambers
parent a3fe4b9980
commit 7762afc497
4 changed files with 126 additions and 1 deletions

View File

@ -0,0 +1,17 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
public class ImpossibleNumberException extends Exception {
public ImpossibleNumberException() {
super();
}
public ImpossibleNumberException(final Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
public class NonNormalizedNumberException extends Exception {
private final String originalNumber;
private final String normalizedNumber;
public NonNormalizedNumberException(final String originalNumber, final String normalizedNumber) {
this.originalNumber = originalNumber;
this.normalizedNumber = normalizedNumber;
}
public String getOriginalNumber() {
return originalNumber;
}
public String getNormalizedNumber() {
return normalizedNumber;
}
}

View File

@ -4,7 +4,10 @@
*/
package org.whispersystems.textsecuregcm.util;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
@ -28,6 +31,8 @@ public class Util {
private static final Pattern COUNTRY_CODE_PATTERN = Pattern.compile("^\\+([17]|2[07]|3[0123469]|4[013456789]|5[12345678]|6[0123456]|8[1246]|9[0123458]|\\d{3})");
private static final PhoneNumberUtil PHONE_NUMBER_UTIL = PhoneNumberUtil.getInstance();
public static byte[] getContactToken(String number) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA1");
@ -41,7 +46,32 @@ public class Util {
}
public static boolean isValidNumber(String number) {
return number.matches("^\\+[0-9]+") && PhoneNumberUtil.getInstance().isPossibleNumber(number, null);
return number.matches("^\\+[0-9]+") && PHONE_NUMBER_UTIL.isPossibleNumber(number, null);
}
/**
* Checks that the given number is a valid, E164-normalized phone number.
*
* @param number the number to check
*
* @throws ImpossibleNumberException if the given number is not a valid phone number at all
* @throws NonNormalizedNumberException if the given number is a valid phone number, but isn't E164-normalized
*/
public static void requireNormalizedNumber(final String number) throws ImpossibleNumberException, NonNormalizedNumberException {
if (!PHONE_NUMBER_UTIL.isPossibleNumber(number, null)) {
throw new ImpossibleNumberException();
}
try {
final PhoneNumber phoneNumber = PHONE_NUMBER_UTIL.parse(number, null);
final String normalizedNumber = PHONE_NUMBER_UTIL.format(phoneNumber, PhoneNumberFormat.E164);
if (!number.equals(normalizedNumber)) {
throw new NonNormalizedNumberException(number, normalizedNumber);
}
} catch (final NumberParseException e) {
throw new ImpossibleNumberException(e);
}
}
public static String getCountryCode(String number) {

View File

@ -5,14 +5,20 @@
package org.whispersystems.textsecuregcm.tests.util;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.whispersystems.textsecuregcm.util.ImpossibleNumberException;
import org.whispersystems.textsecuregcm.util.NonNormalizedNumberException;
import org.whispersystems.textsecuregcm.util.Util;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ValidNumberTest {
@ -53,4 +59,51 @@ class ValidNumberTest {
Arguments.of("+689123456", true)
);
}
@ParameterizedTest
@ValueSource(strings = {
"+447700900111",
"+14151231234",
"+71234567890",
"+447535742222",
"+4915174108888",
"+298123456",
"+299123456",
"+376123456",
"+68512345",
"+689123456"})
void requireNormalizedNumber(final String number) {
assertDoesNotThrow(() -> Util.requireNormalizedNumber(number));
}
@Test
void requireNormalizedNumberNull() {
assertThrows(ImpossibleNumberException.class, () -> Util.requireNormalizedNumber(null));
}
@ParameterizedTest
@ValueSource(strings = {
"Definitely not a phone number at all",
"+141512312341",
"+712345678901",
"+4475357422221",
"+491517410888811111",
"71234567890",
"001447535742222",
"+1415123123a"
})
void requireNormalizedNumberImpossibleNumber(final String number) {
assertThrows(ImpossibleNumberException.class, () -> Util.requireNormalizedNumber(number));
}
@ParameterizedTest
@ValueSource(strings = {
"+4407700900111",
"+1 415 123 1234",
"+1 (415) 123-1234",
"+1 415)123-1234",
" +14151231234"})
void requireNormalizedNumberNonNormalized(final String number) {
assertThrows(NonNormalizedNumberException.class, () -> Util.requireNormalizedNumber(number));
}
}