From 5f13e6f0e345b5facca0bde5d8a077b3c4586398 Mon Sep 17 00:00:00 2001 From: Gaetano Miglionico Date: Thu, 6 Feb 2025 08:20:08 +0000 Subject: [PATCH] [SELC-6304] feat: added checks on product phases and roles (#504) --- .../dashboard/core/UserV2ServiceImpl.java | 46 +++- .../dashboard/core/UserV2ServiceImplTest.java | 199 +++++++++++++++++- 2 files changed, 232 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImpl.java b/core/src/main/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImpl.java index 432287518..86d12c16e 100644 --- a/core/src/main/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImpl.java +++ b/core/src/main/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImpl.java @@ -11,14 +11,18 @@ import it.pagopa.selfcare.dashboard.connector.model.product.mapper.ProductMapper; import it.pagopa.selfcare.dashboard.connector.model.user.*; import it.pagopa.selfcare.dashboard.core.exception.InvalidOnboardingStatusException; +import it.pagopa.selfcare.dashboard.core.exception.InvalidProductRoleException; import it.pagopa.selfcare.onboarding.common.PartyRole; +import it.pagopa.selfcare.product.entity.PHASE_ADDITION_ALLOWED; import it.pagopa.selfcare.product.entity.Product; +import it.pagopa.selfcare.product.entity.ProductRole; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import java.util.*; +import java.util.stream.Collectors; @Slf4j @@ -136,7 +140,8 @@ public String createUsers(String institutionId, String productId, UserToCreate u log.trace("createOrUpdateUserByFiscalCode start"); log.debug("createOrUpdateUserByFiscalCode userDto = {}", userDto); Institution institution = verifyOnboardingStatus(institutionId, productId); - List role = retrieveRole(productId, userDto.getProductRoles(), userDto.getRole()); + Product product = verifyProductPhasesAndRoles(productId, institution.getInstitutionType(), userDto.getRole(), userDto.getProductRoles()); + List role = retrieveRole(product, userDto.getProductRoles(), userDto.getRole()); String userId = userApiConnector.createOrUpdateUserByFiscalCode(institution, productId, userDto, role); log.trace("createOrUpdateUserByFiscalCode end"); return userId; @@ -148,7 +153,8 @@ public void addUserProductRoles(String institutionId, String productId, String u log.debug("createOrUpdateUserByUserId userId = {}", userId); Institution institution = verifyOnboardingStatus(institutionId, productId); PartyRole partyRole = Optional.ofNullable(role).map(PartyRole::valueOf).orElse(null); - List roleDto = retrieveRole(productId, productRoles, partyRole); + Product product = verifyProductPhasesAndRoles(productId, institution.getInstitutionType(), partyRole, productRoles); + List roleDto = retrieveRole(product, productRoles, partyRole); userApiConnector.createOrUpdateUserByUserId(institution, productId, userId, roleDto); log.trace("createOrUpdateUserByUserId end"); } @@ -163,14 +169,46 @@ private Institution verifyOnboardingStatus(String institutionId, String productI else return institution; } + /** + *

Get the product and verify if his phasesAdditionAllowed field allow to add users directly from dashboard with the specified partyRole and productRoles + * or throw an exception.

+ * + *

Dashboard can add the user directly if the phasesAdditionAllowed contains the "dashboard" string else additional steps + * needs to be performed by the user (ex: sign additional documentation) and an onboarding procedure is required

+ * + *

All the productRoles must be present in the roles of the ProductRoleInfo

+ * + * @param productId product id + * @param institutionType institution type + * @param partyRole role to use + * @param productRoles a set of productRoles + * @return the product + * @throws InvalidProductRoleException if dashboard can not add the user directly with the partyRole specified + */ + private Product verifyProductPhasesAndRoles(String productId, String institutionType, PartyRole partyRole, Set productRoles) { + final Product product = productsConnector.getProduct(productId); + return Optional.ofNullable(partyRole) + // If partyRole is present ==> get the ProductRoleInfo + .map(pr -> product.getRoleMappings(institutionType).get(pr)) + // Check if phasesAdditionAllowed contains the "dashboard" string + .filter(pri -> pri.getPhasesAdditionAllowed().stream().anyMatch(PHASE_ADDITION_ALLOWED.DASHBOARD.value::equals)) + // Obtain a set of validRoles from the product role info + .map(pri -> pri.getRoles().stream().map(ProductRole::getCode).collect(Collectors.toSet())) + // Check if all the productRoles in input are validRoles + .filter(validRoles -> validRoles.containsAll(productRoles)) + // If all the previous filters are successful ==> Return the product + .map(i -> product) + // If any of the previous filters fail ==> throw exception + .orElseThrow(() -> new InvalidProductRoleException("The product doesn't allow adding users directly with these role and productRoles")); + } + /** * This method is used to retrieve a list of roles for a given product. * It maps each product role to a CreateUserDto.Role object, which includes the label and party role. * To retrieve the party role, it uses the roleMappings of the product filtering by a white list of party roles (Only SUB_DELEGATE and OPERATOR are allowed * as Role to be assigned to a user in add Users ProductRoles operation). */ - private List retrieveRole(String productId, Set productRoles, PartyRole partyRole) { - Product product = productsConnector.getProduct(productId); + private List retrieveRole(Product product, Set productRoles, PartyRole partyRole) { return productRoles.stream().map(productRole -> { CreateUserDto.Role role = new CreateUserDto.Role(); role.setProductRole(productRole); diff --git a/core/src/test/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImplTest.java b/core/src/test/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImplTest.java index af8c0cfe0..2a3c96da8 100644 --- a/core/src/test/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImplTest.java +++ b/core/src/test/java/it/pagopa/selfcare/dashboard/core/UserV2ServiceImplTest.java @@ -11,6 +11,7 @@ import it.pagopa.selfcare.dashboard.connector.model.institution.RelationshipState; import it.pagopa.selfcare.dashboard.connector.model.user.*; import it.pagopa.selfcare.dashboard.core.exception.InvalidOnboardingStatusException; +import it.pagopa.selfcare.dashboard.core.exception.InvalidProductRoleException; import it.pagopa.selfcare.onboarding.common.PartyRole; import it.pagopa.selfcare.product.entity.PHASE_ADDITION_ALLOWED; import it.pagopa.selfcare.product.entity.Product; @@ -289,7 +290,7 @@ void addUserProductRoles_ok() { final String productId = "productId"; final String userId = "userId"; Set productRoles = new HashSet<>(List.of("operator")); - String role = "MANAGER"; + String role = "OPERATOR"; Product product = getProduct(); Institution institution = new Institution(); @@ -301,10 +302,7 @@ void addUserProductRoles_ok() { CreateUserDto.Role roleDto = new CreateUserDto.Role(); roleDto.setProductRole("operator"); roleDto.setLabel("operator"); - roleDto.setPartyRole(PartyRole.MANAGER); - - when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); - when(productsConnectorMock.getProduct(productId)).thenReturn(product); + roleDto.setPartyRole(OPERATOR); when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); when(productsConnectorMock.getProduct(productId)).thenReturn(product); @@ -365,6 +363,97 @@ void addUserProductRoles_invalidOnboardingStatus() { verifyNoInteractions(userApiConnectorMock); } + @Test + void addUserProductRolesWithInvalidPartyRole() { + final String institutionId = "institutionId"; + final String productId = "productId"; + final String userId = "userId"; + final Set productRoles = Set.of("operator"); + final Product product = getProduct(); + + final Institution institution = new Institution(); + final OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductId(productId); + onboardedProduct.setStatus(RelationshipState.ACTIVE); + institution.setOnboarding(List.of(onboardedProduct)); + + when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); + when(productsConnectorMock.getProduct(productId)).thenReturn(product); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.addUserProductRoles(institutionId, productId, userId, productRoles, null), + "The product doesn't allow adding users directly with these role and productRoles"); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.addUserProductRoles(institutionId, productId, userId, productRoles, "MANAGER"), + "The product doesn't allow adding users directly with these role and productRoles"); + } + + @Test + void addUserProductRolesWithInvalidPhasesAdditionAllowed() { + final String institutionId = "institutionId"; + final String productId = "productId"; + final String userId = "userId"; + final Set productRoles = Set.of("manager"); + final String role = "MANAGER"; + + final Product product = getProduct(); + final ProductRoleInfo productRoleInfoManager = new ProductRoleInfo(); + productRoleInfoManager.setPhasesAdditionAllowed(List.of(PHASE_ADDITION_ALLOWED.DASHBOARD_ASYNC.value, PHASE_ADDITION_ALLOWED.ONBOARDING.value)); + final ProductRole pr = new ProductRole(); + pr.setCode("manager"); + pr.setLabel("manager"); + productRoleInfoManager.setRoles(List.of(pr)); + product.setRoleMappings(Map.of(MANAGER, productRoleInfoManager)); + + final Institution institution = new Institution(); + final OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductId(productId); + onboardedProduct.setStatus(RelationshipState.ACTIVE); + institution.setOnboarding(List.of(onboardedProduct)); + + when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); + when(productsConnectorMock.getProduct(productId)).thenReturn(product); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.addUserProductRoles(institutionId, productId, userId, productRoles, role), + "The product doesn't allow adding users directly with these role and productRoles"); + } + + @Test + void addUserProductRolesWithInvalidProductRoles() { + final String institutionId = "institutionId"; + final String productId = "productId"; + final String userId = "userId"; + final Set productRoles = Set.of("operator2", "operator0", "operator1"); + final String role = "OPERATOR"; + + final Product product = getProduct(); + final ProductRoleInfo productRoleInfoOperator = new ProductRoleInfo(); + productRoleInfoOperator.setPhasesAdditionAllowed(List.of(PHASE_ADDITION_ALLOWED.DASHBOARD_ASYNC.value, PHASE_ADDITION_ALLOWED.DASHBOARD.value)); + final ProductRole pr1 = new ProductRole(); + pr1.setCode("operator1"); + pr1.setLabel("operator1"); + final ProductRole pr2 = new ProductRole(); + pr2.setCode("operator2"); + pr2.setLabel("operator2"); + productRoleInfoOperator.setRoles(List.of(pr1, pr2)); + product.setRoleMappings(Map.of(PartyRole.OPERATOR, productRoleInfoOperator)); + + final Institution institution = new Institution(); + final OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductId(productId); + onboardedProduct.setStatus(RelationshipState.ACTIVE); + institution.setOnboarding(List.of(onboardedProduct)); + + when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); + when(productsConnectorMock.getProduct(productId)).thenReturn(product); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.addUserProductRoles(institutionId, productId, userId, productRoles, role), + "The product doesn't allow adding users directly with these role and productRoles"); + } + @Test void createUsersByFiscalCode() { @@ -374,7 +463,7 @@ void createUsersByFiscalCode() { UserToCreate userToCreate = new UserToCreate(); HashSet productRoles = new HashSet<>(); productRoles.add(productRole); - userToCreate.setRole(PartyRole.MANAGER); + userToCreate.setRole(OPERATOR); userToCreate.setProductRoles(productRoles); Product product = getProduct(); @@ -389,7 +478,7 @@ void createUsersByFiscalCode() { CreateUserDto.Role roleDto = new CreateUserDto.Role(); roleDto.setProductRole("operator"); roleDto.setLabel("operator"); - roleDto.setPartyRole(MANAGER); + roleDto.setPartyRole(OPERATOR); when(productsConnectorMock.getProduct(productId)).thenReturn(product); @@ -418,7 +507,7 @@ void createUsersByFiscalCodeWithPartyROle() { UserToCreate userToCreate = new UserToCreate(); HashSet productRoles = new HashSet<>(); productRoles.add(productRole); - userToCreate.setRole(PartyRole.MANAGER); + userToCreate.setRole(OPERATOR); userToCreate.setProductRoles(productRoles); Product product = getProduct(); @@ -432,7 +521,7 @@ void createUsersByFiscalCodeWithPartyROle() { CreateUserDto.Role roleDto = new CreateUserDto.Role(); roleDto.setProductRole("operator"); roleDto.setLabel("operator"); - roleDto.setPartyRole(PartyRole.MANAGER); + roleDto.setPartyRole(OPERATOR); when(productsConnectorMock.getProduct(productId)).thenReturn(product); @@ -470,6 +559,98 @@ void createUsersByFiscalCodeWithOnboardingNotActive() { verifyNoMoreInteractions(userApiConnectorMock); } + @Test + void createUsersByFiscalCodeWithInvalidPartyRole() { + final String institutionId = "institutionId"; + final String productId = "productId"; + final UserToCreate userToCreate = new UserToCreate(); + final Set productRoles = Set.of("manager"); + userToCreate.setRole(MANAGER); + userToCreate.setProductRoles(productRoles); + + final Product product = getProduct(); + + final Institution institution = new Institution(); + final OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductId(productId); + onboardedProduct.setStatus(RelationshipState.ACTIVE); + institution.setOnboarding(List.of(onboardedProduct)); + + when(productsConnectorMock.getProduct(productId)).thenReturn(product); + when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.createUsers(institutionId, productId, userToCreate), + "The product doesn't allow adding users directly with these role and productRoles"); + } + + @Test + void createUsersByFiscalCodeWithInvalidPhasesAdditionAllowed() { + final String institutionId = "institutionId"; + final String productId = "productId"; + final UserToCreate userToCreate = new UserToCreate(); + final Set productRoles = Set.of("manager"); + userToCreate.setRole(MANAGER); + userToCreate.setProductRoles(productRoles); + + final Product product = getProduct(); + final ProductRoleInfo productRoleInfoManager = new ProductRoleInfo(); + productRoleInfoManager.setPhasesAdditionAllowed(List.of(PHASE_ADDITION_ALLOWED.DASHBOARD_ASYNC.value, PHASE_ADDITION_ALLOWED.ONBOARDING.value)); + final ProductRole pr = new ProductRole(); + pr.setCode("manager"); + pr.setLabel("manager"); + productRoleInfoManager.setRoles(List.of(pr)); + product.setRoleMappings(Map.of(MANAGER, productRoleInfoManager)); + + final Institution institution = new Institution(); + final OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductId(productId); + onboardedProduct.setStatus(RelationshipState.ACTIVE); + institution.setOnboarding(List.of(onboardedProduct)); + + when(productsConnectorMock.getProduct(productId)).thenReturn(product); + when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.createUsers(institutionId, productId, userToCreate), + "The product doesn't allow adding users directly with these role and productRoles"); + } + + @Test + void createUsersByFiscalCodeWithInvalidProductRoles() { + final String institutionId = "institutionId"; + final String productId = "productId"; + final UserToCreate userToCreate = new UserToCreate(); + final Set productRoles = Set.of("operator2", "operator0", "operator1"); + userToCreate.setRole(OPERATOR); + userToCreate.setProductRoles(productRoles); + + final Product product = getProduct(); + final ProductRoleInfo productRoleInfoOperator = new ProductRoleInfo(); + productRoleInfoOperator.setPhasesAdditionAllowed(List.of(PHASE_ADDITION_ALLOWED.DASHBOARD_ASYNC.value, PHASE_ADDITION_ALLOWED.DASHBOARD.value)); + final ProductRole pr1 = new ProductRole(); + pr1.setCode("operator1"); + pr1.setLabel("operator1"); + final ProductRole pr2 = new ProductRole(); + pr2.setCode("operator2"); + pr2.setLabel("operator2"); + productRoleInfoOperator.setRoles(List.of(pr1, pr2)); + product.setRoleMappings(Map.of(PartyRole.OPERATOR, productRoleInfoOperator)); + + final Institution institution = new Institution(); + final OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductId(productId); + onboardedProduct.setStatus(RelationshipState.ACTIVE); + institution.setOnboarding(List.of(onboardedProduct)); + + when(productsConnectorMock.getProduct(productId)).thenReturn(product); + when(msCoreConnectorMock.getInstitution(institutionId)).thenReturn(institution); + + assertThrows(InvalidProductRoleException.class, + () -> userV2ServiceImpl.createUsers(institutionId, productId, userToCreate), + "The product doesn't allow adding users directly with these role and productRoles"); + } + private static Product getProduct() { Product product = new Product(); Map map = new EnumMap<>(PartyRole.class);