From 71322eee4a19a7e00c5ef9fd26a642fd4bc70de6 Mon Sep 17 00:00:00 2001 From: Stanimir Penkov Date: Wed, 7 Nov 2018 14:18:55 +0200 Subject: [PATCH 1/6] NY-4043: Endpoint for Auth provider update Signed-off-by: Stanimir Penkov --- .../account/components/StatementsPool.java | 10 +++ .../nynja/account/components/Validator.java | 26 ++++++++ .../account/phone/PhoneNumberNormalizer.java | 39 +++++++++++ .../AccountRepositoryAdditional.java | 3 + .../AccountRepositoryAdditionalImpl.java | 65 +++++++++++++++++++ .../account/services/AccountServiceImpl.java | 41 ++++++++++++ 6 files changed, 184 insertions(+) diff --git a/src/main/java/biz/nynja/account/components/StatementsPool.java b/src/main/java/biz/nynja/account/components/StatementsPool.java index ba8c9d6..9a2f438 100644 --- a/src/main/java/biz/nynja/account/components/StatementsPool.java +++ b/src/main/java/biz/nynja/account/components/StatementsPool.java @@ -33,6 +33,16 @@ public class StatementsPool { return bound; } + public BoundStatement updateAuthenticationProvidersInProfile(UUID profileId, + Set authProvidersSet, Long updatedTimestamp) { + String cql = "UPDATE profile SET authenticationproviders = ?, lastupdatetimestamp = ? WHERE profileid = ? ;"; + Set updatedSet = Collections + .unmodifiableSet(new HashSet(authProvidersSet)); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(updatedSet, updatedTimestamp, + profileId); + return bound; + } + public BoundStatement insertProfileByAuthenticationProvider(UUID profileId, String authPovider, String authProviderType) { String cql = "INSERT INTO profilebyauthenticationprovider (authenticationprovider, authenticationprovidertype, profileid) VALUES (?, ?, ?) ;"; diff --git a/src/main/java/biz/nynja/account/components/Validator.java b/src/main/java/biz/nynja/account/components/Validator.java index baf7eb2..a3513b9 100644 --- a/src/main/java/biz/nynja/account/components/Validator.java +++ b/src/main/java/biz/nynja/account/components/Validator.java @@ -253,6 +253,32 @@ public class Validator { return validateContactInfo(contactDetails.getType(), contactDetails.getValue()); } + public Optional> validateUpdateAuthProviderRequest(String profileId, + AuthProviderDetails oldAuthProvider, AuthProviderDetails updatedAuthProvider) { + Optional> validationResultOldAuthProvider = validateAuthProviderRequest(profileId, + oldAuthProvider); + if (validationResultOldAuthProvider.isPresent()) { + return validationResultOldAuthProvider; + } + return validateAuthProviderRequest(profileId, updatedAuthProvider); + } + + public Optional> validateAuthProviderRequest(String profileId, + AuthProviderDetails authProviderDetails) { + if ((profileId == null) || (profileId.isEmpty())) { + return Optional.of(new ImmutablePair<>(Cause.MISSING_PROFILE_ID, "Missing profile id")); + } + if (!isValidUuid(profileId)) { + return Optional.of(new ImmutablePair<>(Cause.INVALID_PROFILE_ID, "Invalid profile id")); + } + Cause cause = validateAuthProvider(authProviderDetails.getAuthenticationType(), + authProviderDetails.getAuthenticationProvider()); + if (cause != null) { + return Optional.of(new ImmutablePair<>(cause, cause.toString())); + } + return Optional.empty(); + } + public Cause validateDeleteAuthenticationProviderRequest(DeleteAuthenticationProviderRequest request) { if (!isValidUuid(request.getProfileId())) { return Cause.INVALID_PROFILE_ID; diff --git a/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java b/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java index e9b434f..998ccc1 100644 --- a/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java +++ b/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java @@ -8,9 +8,12 @@ import org.springframework.stereotype.Service; import biz.nynja.account.components.Validator; import biz.nynja.account.grpc.AddContactInfoRequest; +import biz.nynja.account.grpc.AuthProviderDetails; +import biz.nynja.account.grpc.AuthenticationType; import biz.nynja.account.grpc.ContactDetails; import biz.nynja.account.grpc.DeleteContactInfoRequest; import biz.nynja.account.grpc.EditContactInfoRequest; +import biz.nynja.account.grpc.UpdateAuthenticationProviderRequest; @Service public class PhoneNumberNormalizer { @@ -56,4 +59,40 @@ public class PhoneNumberNormalizer { request = newRequest; return request; } + + public UpdateAuthenticationProviderRequest normalizePhoneNumbers(UpdateAuthenticationProviderRequest request) { + boolean oldIsPhone = false, updatedIsPhone = false; + AuthProviderDetails normalizedOldNumber = null, normalizedUpdatedNumber = null; + UpdateAuthenticationProviderRequest newRequest = null; + if (request.getOldAuthProvider().getAuthenticationTypeValue() == AuthenticationType.PHONE_VALUE) { + oldIsPhone = true; + // Get the normalized phone number from libphone for the old number + normalizedOldNumber = AuthProviderDetails.newBuilder() + .setAuthenticationType(request.getOldAuthProvider().getAuthenticationType()) + .setAuthenticationProvider(validator + .getNormalizedPhoneNumber(request.getOldAuthProvider().getAuthenticationProvider())) + .build(); + } + if (request.getUpdatedAuthProvider().getAuthenticationTypeValue() == AuthenticationType.PHONE_VALUE) { + updatedIsPhone = true; + // Get the normalized phone number from libphone for the updated number + normalizedUpdatedNumber = AuthProviderDetails.newBuilder() + .setAuthenticationType(request.getUpdatedAuthProvider().getAuthenticationType()) + .setAuthenticationProvider(validator + .getNormalizedPhoneNumber(request.getUpdatedAuthProvider().getAuthenticationProvider())) + .build(); + } + + if (oldIsPhone && updatedIsPhone) { + newRequest = UpdateAuthenticationProviderRequest.newBuilder().setProfileId(request.getProfileId()).setOldAuthProvider(normalizedOldNumber).setUpdatedAuthProvider(normalizedUpdatedNumber).build(); + request = newRequest; + } else if (oldIsPhone) { + newRequest = UpdateAuthenticationProviderRequest.newBuilder().setProfileId(request.getProfileId()).setOldAuthProvider(normalizedOldNumber).setUpdatedAuthProvider(request.getUpdatedAuthProvider()).build(); + request = newRequest; + } else if (updatedIsPhone) { + newRequest = UpdateAuthenticationProviderRequest.newBuilder().setProfileId(request.getProfileId()).setOldAuthProvider(request.getOldAuthProvider()).setUpdatedAuthProvider(normalizedUpdatedNumber).build(); + request = newRequest; + } + return request; + } } diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java index a7dafe4..00c50a7 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java @@ -35,6 +35,9 @@ public interface AccountRepositoryAdditional { boolean addAuthenticationProvider(UUID profileId, AuthenticationProvider authProvider); + boolean updateAuthenticationProvider(UUID profileId, AuthenticationProvider oldAuthProvider, + AuthenticationProvider updatedAuthProvider); + boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider); boolean addContactInfo(UUID accountId, ContactInfo contactInfo); diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 5d89ee1..d193216 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -482,6 +482,71 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio return false; } + @Override + public boolean updateAuthenticationProvider(UUID profileId, AuthenticationProvider oldAuthProvider, + AuthenticationProvider updatedAuthProvider) { + Profile profileToUpdate = profileRepository.findByProfileId(profileId); + if (profileToUpdate == null) { + logger.error("Existing profile with the provided id {} was not found.", profileId); + return false; + } + + Set authProvidersSet = profileToUpdate.getAuthenticationProviders(); + if (authProvidersSet == null) { + logger.error("No existing authentication providers found for profile with id {}", profileId); + return false; + } + if (!authProvidersSet.remove(oldAuthProvider)) { + logger.error( + "Error removing authentication provider from profile {}. Existing authentication provider: {} was not found.", + profileId, oldAuthProvider.toString()); + return false; + } + if (!authProvidersSet.add(updatedAuthProvider)) { + logger.error("Error adding authentication provider to profile {}. {} already exists.", profileId, + updatedAuthProvider.toString()); + return false; + } + + BatchStatement batch = new BatchStatement(); + ResultSet wr = null; + Long timeUpdated = Instant.now().toEpochMilli(); + try { + BoundStatement updateAuthProvidersInProfile = statementsPool + .updateAuthenticationProvidersInProfile(profileId, authProvidersSet, timeUpdated); + batch.add(updateAuthProvidersInProfile); + + BoundStatement insertInProfileByAuthProvider = statementsPool.insertProfileByAuthenticationProvider( + profileId, updatedAuthProvider.getValue(), updatedAuthProvider.getType()); + batch.add(insertInProfileByAuthProvider); + + BoundStatement deleteProfileByAuthProviderStatement = statementsPool + .deleteProfileByAuthenticationProvider(oldAuthProvider.getValue(), oldAuthProvider.getType()); + batch.add(deleteProfileByAuthProviderStatement); + + // Check if there is an account, created using this authentication provider + Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper(oldAuthProvider.getValue(), + oldAuthProvider.getType()); + + // Delete authentication provider from account + if (account != null) { + BoundStatement deleteCreationProviderStatement = statementsPool + .deleteCreationProviderFromAccount(account.getAccountId()); + batch.add(deleteCreationProviderStatement); + } + wr = session.execute(batch); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.error("Exception while adding new authenication provider to profile with id: {}.", profileId); + logger.debug("Exception while adding new authenication provider to profile with id: {}, {}", profileId, + e.getMessage()); + return false; + } + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + @Override public boolean addContactInfo(UUID accountId, ContactInfo contactInfo) { Account accountToUpdate = accountRepository.findByAccountId(accountId); diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index d0c564f..b49855b 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -754,6 +754,47 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas responseObserver.onCompleted(); } + @Override + public void updateAuthenticationProviderForProfile(UpdateAuthenticationProviderRequest request, + StreamObserver responseObserver) { + logger.info("Removing contact info from account requested."); + logger.debug("Removing contact info from account requested: {}", request); + Optional> validationResultEditContactInfo = validator + .validateUpdateAuthProviderRequest(request.getProfileId(), request.getOldAuthProvider(), + request.getUpdatedAuthProvider()); + if (validationResultEditContactInfo.isPresent()) { + prepareErrorStatusResponse(responseObserver, validationResultEditContactInfo.get().getKey(), + validationResultEditContactInfo.get().getValue()); + return; + } + + if (request.getOldAuthProvider().getAuthenticationTypeValue() == AuthenticationType.PHONE_VALUE + || request.getUpdatedAuthProvider().getAuthenticationTypeValue() == AuthenticationType.PHONE_VALUE) { + request = phoneNumberNormalizer.normalizePhoneNumbers(request); + } + + boolean result = accountRepositoryAdditional.updateAuthenticationProvider( + UUID.fromString(request.getProfileId()), + AuthenticationProvider.createAuthenticationProviderFromProto(request.getOldAuthProvider()), + AuthenticationProvider.createAuthenticationProviderFromProto(request.getUpdatedAuthProvider())); + if (result) { + logger.info("Updated auth provider {}:{} to {}:{} for profile {}.", + request.getOldAuthProvider().getAuthenticationType().name(), + request.getOldAuthProvider().getAuthenticationProvider(), + request.getUpdatedAuthProvider().getAuthenticationType().name(), + request.getUpdatedAuthProvider().getAuthenticationProvider(), request.getProfileId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logger.error("Auth provider {}:{} was not updated for profile {}.", + request.getOldAuthProvider().getAuthenticationType().name(), + request.getOldAuthProvider().getAuthenticationProvider(), request.getProfileId()); + prepareErrorStatusResponse(responseObserver, Cause.ERROR_UPDATING_AUTH_PROVIDER, + "Error updating auth provider"); + return; + } + private static void logAndBuildGrpcAccountResponse(StreamObserver responseObserver, AccountResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { -- GitLab From 08a222c1a36ad0e43225c5b3bba8c9db73f51e98 Mon Sep 17 00:00:00 2001 From: Stanimir Penkov Date: Wed, 7 Nov 2018 14:27:56 +0200 Subject: [PATCH 2/6] NY-4043: Change logging message Signed-off-by: Stanimir Penkov --- .../java/biz/nynja/account/services/AccountServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index b49855b..be06e73 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -757,8 +757,8 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override public void updateAuthenticationProviderForProfile(UpdateAuthenticationProviderRequest request, StreamObserver responseObserver) { - logger.info("Removing contact info from account requested."); - logger.debug("Removing contact info from account requested: {}", request); + logger.info("Updating auth provider for profile {} requested.", request.getProfileId()); + logger.debug("Updating auth provider for profile requested: {}", request); Optional> validationResultEditContactInfo = validator .validateUpdateAuthProviderRequest(request.getProfileId(), request.getOldAuthProvider(), request.getUpdatedAuthProvider()); -- GitLab From c8fb96b080cbdc53aa695f7af399ac1f5724b881 Mon Sep 17 00:00:00 2001 From: Stanimir Penkov Date: Wed, 7 Nov 2018 16:50:46 +0200 Subject: [PATCH 3/6] NY-4043: Added check for empty set Signed-off-by: Stanimir Penkov --- .../repositories/AccountRepositoryAdditionalImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index d193216..5a16bb3 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -507,6 +507,12 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio updatedAuthProvider.toString()); return false; } + if (authProvidersSet.isEmpty()) { + logger.error( + "Error updating authentication provider for profile {}. Can not have a profile without auth providers.", + profileId); + return false; + } BatchStatement batch = new BatchStatement(); ResultSet wr = null; -- GitLab From 1e4096767d2b0a08ad3fcacb152084f2d42e4e13 Mon Sep 17 00:00:00 2001 From: Stanimir Penkov Date: Wed, 14 Nov 2018 14:03:30 +0200 Subject: [PATCH 4/6] NY-4043: added additional checks and updated sending error status - added check for equal old and updated auth providers; - added check for existing auth provider. Signed-off-by: Stanimir Penkov --- .../AccountRepositoryAdditionalImpl.java | 4 ++++ .../account/services/AccountServiceImpl.java | 22 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 5a16bb3..9936881 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -490,6 +490,10 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio logger.error("Existing profile with the provided id {} was not found.", profileId); return false; } + if (authenticationProviderAlreadyUsedInProfile(updatedAuthProvider)) { + logger.error("The requested update auth provider for profile {} is already used.", profileId); + return false; + } Set authProvidersSet = profileToUpdate.getAuthenticationProviders(); if (authProvidersSet == null) { diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index be06e73..e2cbdd8 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -42,6 +42,7 @@ import biz.nynja.account.grpc.SearchResponse; import biz.nynja.account.grpc.SearchResultDetails; import biz.nynja.account.grpc.StatusResponse; import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateAuthenticationProviderRequest; import biz.nynja.account.models.Account; import biz.nynja.account.models.AccountByQrCode; import biz.nynja.account.models.AccountByUsername; @@ -759,12 +760,20 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas StreamObserver responseObserver) { logger.info("Updating auth provider for profile {} requested.", request.getProfileId()); logger.debug("Updating auth provider for profile requested: {}", request); - Optional> validationResultEditContactInfo = validator + Optional> validationResultUpdateAuthProvider = validator .validateUpdateAuthProviderRequest(request.getProfileId(), request.getOldAuthProvider(), request.getUpdatedAuthProvider()); - if (validationResultEditContactInfo.isPresent()) { - prepareErrorStatusResponse(responseObserver, validationResultEditContactInfo.get().getKey(), - validationResultEditContactInfo.get().getValue()); + if (validationResultUpdateAuthProvider.isPresent()) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Auth provider was not updated: {}.", validationResultUpdateAuthProvider.get().getValue(), + validationResultUpdateAuthProvider.get().getKey()); + return; + } + + if (request.getOldAuthProvider().equals(request.getUpdatedAuthProvider())) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "The same old and new auth providers requested to update for profile {}.", request.getProfileId(), + Cause.ERROR_UPDATING_AUTH_PROVIDER); return; } @@ -790,8 +799,9 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas logger.error("Auth provider {}:{} was not updated for profile {}.", request.getOldAuthProvider().getAuthenticationType().name(), request.getOldAuthProvider().getAuthenticationProvider(), request.getProfileId()); - prepareErrorStatusResponse(responseObserver, Cause.ERROR_UPDATING_AUTH_PROVIDER, - "Error updating auth provider"); + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Auth provider was not updated for profile {}.", request.getProfileId(), + Cause.ERROR_UPDATING_AUTH_PROVIDER); return; } -- GitLab From 389a1e4940a0f349c242efbaf59afc644e8a556f Mon Sep 17 00:00:00 2001 From: Stanimir Penkov Date: Wed, 14 Nov 2018 14:09:32 +0200 Subject: [PATCH 5/6] NY-4043: Added needed import Signed-off-by: Stanimir Penkov --- src/main/java/biz/nynja/account/components/Validator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/biz/nynja/account/components/Validator.java b/src/main/java/biz/nynja/account/components/Validator.java index a3513b9..fcf8b0c 100644 --- a/src/main/java/biz/nynja/account/components/Validator.java +++ b/src/main/java/biz/nynja/account/components/Validator.java @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import biz.nynja.account.grpc.AddAuthenticationProviderRequest; +import biz.nynja.account.grpc.AuthProviderDetails; import biz.nynja.account.grpc.AuthenticationType; import biz.nynja.account.grpc.ContactDetails; import biz.nynja.account.grpc.ContactType; -- GitLab From e05097fc3bbc8cc96771f7a9df4e66e8656bfeb4 Mon Sep 17 00:00:00 2001 From: Stanimir Penkov Date: Wed, 14 Nov 2018 14:14:50 +0200 Subject: [PATCH 6/6] NY-4043: Code Review Feedback Signed-off-by: Stanimir Penkov --- .../repositories/AccountRepositoryAdditionalImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 9936881..d3e18c0 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -518,6 +518,11 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio return false; } + return updateAuthProviderInDB(profileId, oldAuthProvider, updatedAuthProvider, authProvidersSet); + } + + private boolean updateAuthProviderInDB(UUID profileId, AuthenticationProvider oldAuthProvider, + AuthenticationProvider updatedAuthProvider, Set authProvidersSet) { BatchStatement batch = new BatchStatement(); ResultSet wr = null; Long timeUpdated = Instant.now().toEpochMilli(); -- GitLab