diff --git a/pom.xml b/pom.xml
index 9f00670e423b8a545d04eff3f3d58ca0f4b7e38f..7e8627c9eeb73329ba9122be087751bbd9542278 100644
--- a/pom.xml
+++ b/pom.xml
@@ -111,6 +111,12 @@
+
+ com.googlecode.libphonenumber
+ libphonenumber
+ 8.9.12
+
+
com.fasterxml.jackson.core
jackson-databind
diff --git a/src/main/java/biz/nynja/account/components/Validator.java b/src/main/java/biz/nynja/account/components/Validator.java
index 769307bbb72ddc5cab152613b6cf2d42d4423c40..ff8899b0280ce69cc66557a9cfbadcf1346b4640 100644
--- a/src/main/java/biz/nynja/account/components/Validator.java
+++ b/src/main/java/biz/nynja/account/components/Validator.java
@@ -20,8 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
-
import biz.nynja.account.grpc.AddAuthenticationProviderRequest;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import biz.nynja.account.grpc.AuthProviderDetails;
import biz.nynja.account.grpc.AuthenticationType;
import biz.nynja.account.grpc.CompletePendingAccountCreationRequest;
@@ -126,6 +128,27 @@ public class Validator {
return isValid;
}
+
+ public String getNormalizedPhoneNumber(String authenticationProvider) {
+ String[] provider = authenticationProvider.split(":");
+ String country = provider[0];
+ String phoneNumber = provider[1];
+ logger.info("libphone: New phone number normalization request received - phone number: {}, country code: {}", phoneNumber,
+ country);
+
+ PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+ String normalizedPhoneNumber = "";
+ try {
+ PhoneNumber pn = phoneUtil.parse(phoneNumber, country);
+ normalizedPhoneNumber = Integer.toString(pn.getCountryCode()) +
+ Long.toString(pn.getNationalNumber());
+ logger.info("libphone: Normalized phone number: " + normalizedPhoneNumber);
+ } catch (NumberParseException e) {
+ logger.error("libphone: NumberParseException was thrown: {}", e.toString());
+ logger.debug("libphone: NumberParseException was thrown: {}", e.getCause());
+ }
+ return normalizedPhoneNumber;
+ }
public boolean isUsernameValid(String username) {
diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java
index 437066074ec60cb48b576d808bd0dbab3bdfa37f..c1acf9d644d934d07a0a1c48c10712fda003b57d 100644
--- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java
+++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java
@@ -12,7 +12,6 @@ import org.lognet.springboot.grpc.GRpcService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-
import biz.nynja.account.components.AccountServiceHelper;
import biz.nynja.account.components.Validator;
import biz.nynja.account.grpc.AccountByAccountIdRequest;
@@ -24,6 +23,8 @@ import biz.nynja.account.grpc.AccountsByProfileIdRequest;
import biz.nynja.account.grpc.AccountsList;
import biz.nynja.account.grpc.AccountsResponse;
import biz.nynja.account.grpc.AddAuthenticationProviderRequest;
+import biz.nynja.account.grpc.AuthProviderDetails;
+import biz.nynja.account.grpc.AuthenticationType;
import biz.nynja.account.grpc.CompletePendingAccountCreationRequest;
import biz.nynja.account.grpc.CreateAccountRequest;
import biz.nynja.account.grpc.CreatePendingAccountRequest;
@@ -119,17 +120,26 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
responseObserver.onCompleted();
return;
}
+ if (request.getAuthenticationType() == AuthenticationType.PHONE) {
+ // Get the normalized phone number from libphone
+ AccountByAuthenticationProviderRequest newRequest = AccountByAuthenticationProviderRequest.newBuilder()
+ .setAuthenticationType(request.getAuthenticationType())
+ .setAuthenticationIdentifier(validator.getNormalizedPhoneNumber(request.getAuthenticationIdentifier())).build();
+ request = newRequest;
+ }
Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper(
request.getAuthenticationIdentifier(), request.getAuthenticationType().toString());
+
if (account == null) {
+
logger.debug("No matching accounts found for authetntication provider {}: {}",
request.getAuthenticationType(), request.getAuthenticationIdentifier());
responseObserver.onNext(AccountResponse.newBuilder()
.setError(ErrorResponse.newBuilder().setCause(Cause.ACCOUNT_NOT_FOUND)).build());
responseObserver.onCompleted();
return;
- }
+ }
AccountResponse response = AccountResponse.newBuilder().setAccountDetails(account.toProto()).build();
logger.debug("Found result for account by authentication provider {}: {}: \"{}\"",
@@ -258,6 +268,11 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
PendingAccount pendingAccount = PendingAccount.fromProto(request);
+ if (request.getAuthenticationType() == AuthenticationType.PHONE) {
+ // Get the normalized phone number from libphone
+ pendingAccount.setAuthenticationProvider(validator.getNormalizedPhoneNumber(request.getAuthenticationProvider()));
+ }
+
pendingAccount.setAccountId(UUID.randomUUID());
pendingAccount.setProfileId(UUID.randomUUID());
pendingAccount.setCreationTimestamp(new Date().getTime());
@@ -290,9 +305,8 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
return;
}
- if (request.getUsername() != null && !request.getUsername().trim().isEmpty()
- && accountRepositoryAdditional.foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()),
- request.getUsername())) {
+ if (request.getUsername() != null && !request.getUsername().trim().isEmpty() && accountRepositoryAdditional
+ .foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), request.getUsername())) {
responseObserver.onNext(AccountResponse.newBuilder()
.setError(ErrorResponse.newBuilder().setCause(Cause.USERNAME_ALREADY_USED)).build());
responseObserver.onCompleted();
@@ -340,8 +354,8 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
Cause cause = validator.validateUpdateProfileRequest(request);
if (cause != null) {
- responseObserver
- .onNext(UpdateProfileResponse.newBuilder().setError(ErrorResponse.newBuilder().setCause(cause)).build());
+ responseObserver.onNext(
+ UpdateProfileResponse.newBuilder().setError(ErrorResponse.newBuilder().setCause(cause)).build());
responseObserver.onCompleted();
return;
}
@@ -383,9 +397,8 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
return;
}
- if (request.getUsername() != null && !request.getUsername().trim().isEmpty()
- && accountRepositoryAdditional.foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()),
- request.getUsername())) {
+ if (request.getUsername() != null && !request.getUsername().trim().isEmpty() && accountRepositoryAdditional
+ .foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), request.getUsername())) {
responseObserver.onNext(AccountResponse.newBuilder()
.setError(ErrorResponse.newBuilder().setCause(Cause.USERNAME_ALREADY_USED)).build());
responseObserver.onCompleted();
@@ -422,8 +435,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
boolean wasAccountDeleted = accountRepositoryAdditional.deleteAccount(UUID.fromString(request.getAccountId()));
if (wasAccountDeleted) {
- responseObserver.onNext(StatusResponse.newBuilder()
- .setStatus("SUCCESS").build());
+ responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build());
responseObserver.onCompleted();
return;
}
@@ -447,8 +459,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
boolean wasProfileDeleted = accountRepositoryAdditional.deleteProfile(UUID.fromString(request.getProfileId()));
if (wasProfileDeleted) {
logger.info("The profile was deleted successfully.");
- responseObserver.onNext(StatusResponse.newBuilder()
- .setStatus("SUCCESS").build());
+ responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build());
responseObserver.onCompleted();
return;
}
@@ -491,6 +502,20 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas
responseObserver.onCompleted();
return;
}
+
+ if (request.getAuthenticationProvider().getAuthenticationType() == AuthenticationType.PHONE) {
+ // Get the normalized phone number from libphone
+ AuthProviderDetails newAuthProviderDetails = AuthProviderDetails.newBuilder()
+ .setAuthenticationType(request.getAuthenticationProvider().getAuthenticationType())
+ .setAuthenticationProvider(validator.getNormalizedPhoneNumber(request.getAuthenticationProvider().getAuthenticationProvider()))
+ .build();
+ AddAuthenticationProviderRequest newRequest = AddAuthenticationProviderRequest.newBuilder()
+ .setProfileId(request.getProfileId())
+ .setAuthenticationProvider(newAuthProviderDetails).build();
+ request = newRequest;
+ }
+
+
// Make sure that the requested profile id for update exists in DB.
Profile profile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId()));
if (profile == null) {
diff --git a/src/test/java/biz/nynja/account/components/LibphoneNormalizationParameterizedTest.java b/src/test/java/biz/nynja/account/components/LibphoneNormalizationParameterizedTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..19a76cd06f730585b89a6c201e62a681a1e932bc
--- /dev/null
+++ b/src/test/java/biz/nynja/account/components/LibphoneNormalizationParameterizedTest.java
@@ -0,0 +1,71 @@
+package biz.nynja.account.components;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestContextManager;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.rules.SpringClassRule;
+import org.springframework.test.context.junit4.rules.SpringMethodRule;
+
+import biz.nynja.account.repositories.AccountRepository;
+import biz.nynja.account.services.AccountServiceImpl;
+
+// ================================================================
+// This test is not part of the build but we consider it useful
+// and would like to keep it for future use.
+// ===============================================================
+
+@RunWith(Parameterized.class)
+@TestExecutionListeners({})
+@ContextConfiguration(classes = { AccountServiceImpl.class })
+public class LibphoneNormalizationParameterizedTest {
+
+ @Autowired
+ private Validator validator;
+
+ private String expectedPhoneNumber;
+ private String inputPhoneNumber;
+
+ @ClassRule
+ public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
+
+ @Rule
+ public final SpringMethodRule springMethodRule = new SpringMethodRule();
+
+ public LibphoneNormalizationParameterizedTest(String expectedPhoneNumber, String inputPhoneNumber) {
+ this.expectedPhoneNumber = expectedPhoneNumber;
+ this.inputPhoneNumber = inputPhoneNumber;
+ }
+
+ @Parameterized.Parameters
+ public static Collection