diff --git a/src/main/java/biz/nynja/account/components/StatementsPool.java b/src/main/java/biz/nynja/account/components/StatementsPool.java index ba8c9d66456df3ab3381b998c21acb7c3a1a562d..9a2f4381e75ea398c7dfe2b56cd8c5c8761421cc 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 baf7eb282b22daa4073a624534a124de1413beca..fcf8b0c143fe1c8b450cb016ea7f11a6ecf3d28a 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; @@ -253,6 +254,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 e9b434f1703bf22c686f21508a91a5804661b42d..998ccc1028efa2b2ecd40505ae530d14c1507536 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 a7dafe430ca3330610803c810f179240f001ff4c..00c50a7fdeb52f0287204102948154c7a2bfaae8 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 5d89ee1bd874aa471cb19e71b0b1b2c64952e616..d3e18c04451e86698dd1fde758e7cfbd6b35fab7 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -482,6 +482,86 @@ 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; + } + 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) { + 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; + } + if (authProvidersSet.isEmpty()) { + logger.error( + "Error updating authentication provider for profile {}. Can not have a profile without auth providers.", + profileId); + 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(); + 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 d0c564f061a559b6e263eebca20df71158979a48..e2cbdd8702c40e820178122cec888970d49f847c 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; @@ -754,6 +755,56 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas responseObserver.onCompleted(); } + @Override + public void updateAuthenticationProviderForProfile(UpdateAuthenticationProviderRequest request, + StreamObserver responseObserver) { + logger.info("Updating auth provider for profile {} requested.", request.getProfileId()); + logger.debug("Updating auth provider for profile requested: {}", request); + Optional> validationResultUpdateAuthProvider = validator + .validateUpdateAuthProviderRequest(request.getProfileId(), request.getOldAuthProvider(), + request.getUpdatedAuthProvider()); + 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; + } + + 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()); + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Auth provider was not updated for profile {}.", request.getProfileId(), + Cause.ERROR_UPDATING_AUTH_PROVIDER); + return; + } + private static void logAndBuildGrpcAccountResponse(StreamObserver responseObserver, AccountResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) {