From 5f322824ac9f48000aabcf93707e992b7fd4c3c6 Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Sat, 16 Jul 2016 17:23:31 -0400 Subject: [PATCH] Use Google's libphonenumber to perform more robust phone number validation and to convert all phone numbers to E164 before storing them. Closes #45 --- pom.xml | 5 ++ .../net/frozenorb/apiv3/model/PhoneIntel.java | 59 ++++++++++++------- .../java/net/frozenorb/apiv3/model/User.java | 20 +++++-- .../net/frozenorb/apiv3/util/PhoneUtils.java | 26 +++++--- 4 files changed, 78 insertions(+), 32 deletions(-) diff --git a/pom.xml b/pom.xml index 04812be..d8ccefb 100644 --- a/pom.xml +++ b/pom.xml @@ -101,6 +101,11 @@ gson 2.7 + + com.googlecode.libphonenumber + libphonenumber + 7.4.5 + diff --git a/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java b/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java index 980fe94..0b294ac 100644 --- a/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java +++ b/src/main/java/net/frozenorb/apiv3/model/PhoneIntel.java @@ -7,6 +7,7 @@ import fr.javatic.mongo.jacksonCodec.objectId.Id; import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.util.PhoneUtils; import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.ZangUtils; import net.frozenorb.apiv3.zang.ZangResult; @@ -31,32 +32,50 @@ public final class PhoneIntel { } public static void findById(String id, SingleResultCallback callback) { - phoneIntelCollection.find(new Document("_id", id)).first(SyncUtils.vertxWrap(callback)); + String e164Phone = PhoneUtils.toE164(id); + + if (e164Phone == null) { + callback.onResult(null, null); + } else { + phoneIntelCollection.find(new Document("_id", id)).first(SyncUtils.vertxWrap(callback)); + } } public static void findOrCreateById(String id, SingleResultCallback callback) { - findById(id, (existingPhoneIntel, error) -> { + String e164Phone = PhoneUtils.toE164(id); + + if (e164Phone == null) { + callback.onResult(null, null); + return; + } + + findById(e164Phone, (existingPhoneIntel, error) -> { if (error != null) { callback.onResult(null, error); - } else if (existingPhoneIntel != null) { - callback.onResult(existingPhoneIntel, null); - } else { - ZangUtils.getCarrierInfo(id, (zangResult, error2) -> { - if (error2 != null) { - callback.onResult(null, error2); - } else { - PhoneIntel newPhoneIntel = new PhoneIntel(id, zangResult); - - phoneIntelCollection.insertOne(newPhoneIntel, SyncUtils.vertxWrap((ignored, error3) -> { - if (error3 != null) { - callback.onResult(null, error3); - } else { - callback.onResult(newPhoneIntel, null); - } - })); - } - }); + return; } + + if (existingPhoneIntel != null) { + callback.onResult(existingPhoneIntel, null); + return; + } + + ZangUtils.getCarrierInfo(e164Phone, (zangResult, error2) -> { + if (error2 != null) { + callback.onResult(null, error2); + return; + } + + PhoneIntel newPhoneIntel = new PhoneIntel(e164Phone, zangResult); + + phoneIntelCollection.insertOne(newPhoneIntel, SyncUtils.vertxWrap((ignored, error3) -> { + if (error3 != null) { + callback.onResult(null, error3); + } else { + callback.onResult(newPhoneIntel, null); + } + })); + }); }); } diff --git a/src/main/java/net/frozenorb/apiv3/model/User.java b/src/main/java/net/frozenorb/apiv3/model/User.java index 0b7dd5f..ccaf005 100644 --- a/src/main/java/net/frozenorb/apiv3/model/User.java +++ b/src/main/java/net/frozenorb/apiv3/model/User.java @@ -117,10 +117,16 @@ public final class User { } public static void findByPhone(String phoneNumber, SingleResultCallback callback) { - usersCollection.find(new Document("$or", ImmutableList.of( - new Document("phone", phoneNumber), - new Document("pendingPhone", phoneNumber) - ))).first(SyncUtils.vertxWrap(callback)); + String e164Phone = PhoneUtils.toE164(phoneNumber); + + if (e164Phone == null) { + callback.onResult(null, null); + } else { + usersCollection.find(new Document("$or", ImmutableList.of( + new Document("phone", phoneNumber), + new Document("pendingPhone", phoneNumber) + ))).first(SyncUtils.vertxWrap(callback)); + } } public static void findByEmail(String email, SingleResultCallback callback) { @@ -536,7 +542,11 @@ public final class User { } public void startPhoneRegistration(String phoneNumber) { - this.pendingPhone = phoneNumber; + String e164Phone = PhoneUtils.toE164(phoneNumber); + + if (e164Phone == null) return; + + this.pendingPhone = e164Phone; this.pendingPhoneToken = String.valueOf(new Random().nextInt(999999 - 100000) + 100000); this.pendingPhoneTokenSetAt = Instant.now(); } diff --git a/src/main/java/net/frozenorb/apiv3/util/PhoneUtils.java b/src/main/java/net/frozenorb/apiv3/util/PhoneUtils.java index dbbb78e..dc61d0e 100644 --- a/src/main/java/net/frozenorb/apiv3/util/PhoneUtils.java +++ b/src/main/java/net/frozenorb/apiv3/util/PhoneUtils.java @@ -1,19 +1,31 @@ package net.frozenorb.apiv3.util; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; import lombok.experimental.UtilityClass; -import java.util.regex.Pattern; - @UtilityClass public class PhoneUtils { - private static final Pattern VALID_PHONE_PATTERN = Pattern.compile( - "^\\+?[1-9]\\d{1,14}$", - Pattern.CASE_INSENSITIVE - ); + public static final String DEFAULT_COUNTRY_CODE = "US"; + private static final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); public static boolean isValidPhone(String phoneNumber) { - return phoneNumber != null && VALID_PHONE_PATTERN.matcher(phoneNumber).matches(); + try { + return phoneNumber != null && phoneUtil.isValidNumber(phoneUtil.parse(phoneNumber, DEFAULT_COUNTRY_CODE)); + } catch (NumberParseException ex) { + return false; + } + } + + public static String toE164(String phoneNumber) { + try { + Phonenumber.PhoneNumber number = phoneUtil.parse(phoneNumber, DEFAULT_COUNTRY_CODE); + return phoneUtil.format(number, PhoneNumberUtil.PhoneNumberFormat.E164); + } catch (NumberParseException ex) { + return ""; + } } } \ No newline at end of file