diff --git a/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionService.java b/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionService.java index e0c30a24..fe4ed9dc 100644 --- a/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionService.java +++ b/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionService.java @@ -20,7 +20,7 @@ public interface InstitutionService { void onboardingProductV2(OnboardingData onboardingData); - void onboardingCompanyV2(OnboardingData onboardingData); + void onboardingCompanyV2(OnboardingData onboardingData, String userFiscalCode); void onboardingProduct(OnboardingData onboardingData); diff --git a/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImpl.java b/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImpl.java index a19079c1..b4d42c64 100644 --- a/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImpl.java +++ b/core/src/main/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImpl.java @@ -68,6 +68,8 @@ class InstitutionServiceImpl implements InstitutionService { public static final String ONE_OTHER_PARAMETER_PROVIDED = "At least one other parameter must be provided along with productId"; private static final String REQUIRED_AGGREGATE_INSTITUTIONS = "Aggregate institutions are required if given institution is an Aggregator"; + + private static final String ONBOARDING_COMPANY_NOT_ALLOWED = "The selected business does not belong to the user"; static final String DESCRIPTION_TO_REPLACE_REGEX = " - COMUNE"; private final OnboardingMsConnector onboardingMsConnector; private final PartyConnector partyConnector; @@ -118,13 +120,56 @@ public void onboardingPaAggregator(OnboardingData onboardingData) { } @Override - public void onboardingCompanyV2(OnboardingData onboardingData) { + public void onboardingCompanyV2(OnboardingData onboardingData, String userFiscalCode) { log.trace("onboardingProductAsync start"); log.debug("onboardingProductAsync onboardingData = {}", onboardingData); + verifyIfUserIsManagerOfBusiness(onboardingData.getTaxCode(), userFiscalCode, onboardingData.getOrigin()); onboardingMsConnector.onboardingCompany(onboardingData); log.trace("onboarding end"); } + private void verifyIfUserIsManagerOfBusiness(String businessTaxCode, String userFiscalCode, String origin) { + switch (Origin.fromValue(origin)) { + case INFOCAMERE: + verifyIfUserIsManagerOfBusinessOnInfocamere(businessTaxCode, userFiscalCode); + break; + case ADE: + verifyIfUserIsManagerOfBusinessOnAde(businessTaxCode, userFiscalCode); + break; + default: + log.error("Origin {} is not supported", origin); + throw new InvalidRequestException("Origin not supported"); + } + } + + private void verifyIfUserIsManagerOfBusinessOnInfocamere(String businessTaxCode, String userFiscalCode) { + log.debug(LogUtils.CONFIDENTIAL_MARKER, "Checking if user with fiscal code {} is manager of business with tax code {} on Infocamere", + userFiscalCode, businessTaxCode); + InstitutionInfoIC userBusinesses = partyRegistryProxyConnector.getInstitutionsByUserFiscalCode(userFiscalCode); + if (!isICBusinessRelatedToUser(userBusinesses, businessTaxCode)) { + log.error("User is not authorized to onboard business with tax code {}", businessTaxCode); + throw new OnboardingNotAllowedException(ONBOARDING_COMPANY_NOT_ALLOWED); + } + } + + private boolean isICBusinessRelatedToUser(InstitutionInfoIC institutionInfoIC, String businessTaxCode) { + return institutionInfoIC != null + && !CollectionUtils.isEmpty(institutionInfoIC.getBusinesses()) + && institutionInfoIC.getBusinesses() + .stream() + .anyMatch(business -> Objects.equals(business.getBusinessTaxId(), businessTaxCode)); + } + + private void verifyIfUserIsManagerOfBusinessOnAde(String businessTaxCode, String userFiscalCode) { + log.debug(LogUtils.CONFIDENTIAL_MARKER, "Checking if user with fiscal code {} is manager of business with tax code {} on ADE", + userFiscalCode, businessTaxCode); + MatchInfoResult matchInfoResult = partyRegistryProxyConnector.matchInstitutionAndUser(businessTaxCode, userFiscalCode); + if (Objects.isNull(matchInfoResult) || !matchInfoResult.isVerificationResult()) { + log.error("User is not authorized to onboard business with tax code {}", businessTaxCode); + throw new OnboardingNotAllowedException(ONBOARDING_COMPANY_NOT_ALLOWED); + } + } + @Override public void onboardingProduct(OnboardingData onboardingData) { log.trace("onboarding start"); diff --git a/core/src/test/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImplTest.java b/core/src/test/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImplTest.java index 6a3c8ac8..672b3a88 100644 --- a/core/src/test/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImplTest.java +++ b/core/src/test/java/it/pagopa/selfcare/onboarding/core/InstitutionServiceImplTest.java @@ -148,18 +148,118 @@ void onboardingProductAsync() { @Test - void onboardingCompanyV2() { + void onboardingCompanyV2SuccessfullyWhenBusinessIsFoundOnInfocamere() { // given OnboardingData onboardingData = mockInstance(new OnboardingData(), "setInstitutionType", "setUsers"); + onboardingData.setOrigin(Origin.INFOCAMERE.getValue()); + onboardingData.setInstitutionType(InstitutionType.PG); + onboardingData.setUsers(List.of(dummyManager, dummyDelegate)); + String managerTaxCode = dummyManager.getTaxCode(); + + InstitutionInfoIC institutionInfoIC = new InstitutionInfoIC(); + institutionInfoIC.setLegalTaxId(managerTaxCode); + BusinessInfoIC businessInfoIC = new BusinessInfoIC(); + businessInfoIC.setBusinessName("businessName"); + businessInfoIC.setBusinessTaxId(onboardingData.getTaxCode()); + institutionInfoIC.setBusinesses(List.of(businessInfoIC)); + + when(partyRegistryProxyConnectorMock.getInstitutionsByUserFiscalCode(anyString())) + .thenReturn(institutionInfoIC); + // when + institutionService.onboardingCompanyV2(onboardingData, managerTaxCode); + // then + verify(partyRegistryProxyConnectorMock, times(1)) + .getInstitutionsByUserFiscalCode(managerTaxCode); + verify(partyRegistryProxyConnectorMock, times(0)) + .matchInstitutionAndUser(onboardingData.getTaxCode(), managerTaxCode); + verify(onboardingMsConnector, times(1)) + .onboardingCompany(any()); + } + + @Test + void onboardingCompanyV2SuccesfullyWhenBusinessIsFoundOnAde() { + // given + OnboardingData onboardingData = mockInstance(new OnboardingData(), "setInstitutionType", "setUsers"); + onboardingData.setOrigin(Origin.ADE.getValue()); onboardingData.setInstitutionType(InstitutionType.PG); onboardingData.setUsers(List.of(dummyManager, dummyDelegate)); + String managerTaxCode = dummyManager.getTaxCode(); + + MatchInfoResult matchInfoResult = new MatchInfoResult(); + matchInfoResult.setVerificationResult(true); + when(partyRegistryProxyConnectorMock.matchInstitutionAndUser(onboardingData.getTaxCode(), managerTaxCode)) + .thenReturn(matchInfoResult); // when - institutionService.onboardingCompanyV2(onboardingData); + institutionService.onboardingCompanyV2(onboardingData, managerTaxCode); // then + verify(partyRegistryProxyConnectorMock, times(0)) + .getInstitutionsByUserFiscalCode(managerTaxCode); + verify(partyRegistryProxyConnectorMock, times(1)) + .matchInstitutionAndUser(onboardingData.getTaxCode(), managerTaxCode); verify(onboardingMsConnector, times(1)) .onboardingCompany(any()); } + @Test + void onboardingCompanyV2NotAllowedWhenBusinessIsNotRetrievedFromInfocamere() { + // given + OnboardingData onboardingData = mockInstance(new OnboardingData(), "setInstitutionType", "setUsers"); + onboardingData.setOrigin(Origin.INFOCAMERE.getValue()); + onboardingData.setInstitutionType(InstitutionType.PG); + onboardingData.setUsers(List.of(dummyManager, dummyDelegate)); + String managerTaxCode = dummyManager.getTaxCode(); + + when(partyRegistryProxyConnectorMock.getInstitutionsByUserFiscalCode(managerTaxCode)) + .thenReturn(new InstitutionInfoIC()); + // when + Assertions.assertThrows(OnboardingNotAllowedException.class, () -> institutionService.onboardingCompanyV2(onboardingData, managerTaxCode)); + // then + verify(partyRegistryProxyConnectorMock, times(1)) + .getInstitutionsByUserFiscalCode(managerTaxCode); + verify(partyRegistryProxyConnectorMock, times(0)) + .matchInstitutionAndUser(onboardingData.getTaxCode(), managerTaxCode); + verify(onboardingMsConnector, times(0)) + .onboardingCompany(any()); + } + + @Test + void onboardingCompanyV2NotAllowedWhenBusinessIsNotRetrievedFromAde() { + // given + OnboardingData onboardingData = mockInstance(new OnboardingData(), "setInstitutionType", "setUsers"); + onboardingData.setOrigin(Origin.ADE.getValue()); + onboardingData.setInstitutionType(InstitutionType.PG); + onboardingData.setUsers(List.of(dummyManager, dummyDelegate)); + String managerTaxCode = dummyManager.getTaxCode(); + + when(partyRegistryProxyConnectorMock.matchInstitutionAndUser(onboardingData.getTaxCode(), managerTaxCode)) + .thenReturn(new MatchInfoResult()); + // when + Assertions.assertThrows(OnboardingNotAllowedException.class, () -> institutionService.onboardingCompanyV2(onboardingData, managerTaxCode)); + // then + verify(partyRegistryProxyConnectorMock, times(0)) + .getInstitutionsByUserFiscalCode(managerTaxCode); + verify(partyRegistryProxyConnectorMock, times(1)) + .matchInstitutionAndUser(onboardingData.getTaxCode(), managerTaxCode); + verify(onboardingMsConnector, times(0)) + .onboardingCompany(any()); + } + + @Test + void onboardingCompanyV2NotAllowedWhenOriginIsNotAllowed() { + OnboardingData onboardingData = mockInstance(new OnboardingData(), "setInstitutionType", "setUsers"); + onboardingData.setOrigin("IPA"); + +// when + Assertions.assertThrows(InvalidRequestException.class, () -> institutionService.onboardingCompanyV2(onboardingData, dummyManager.getTaxCode())); + // then + verify(partyRegistryProxyConnectorMock, times(0)) + .getInstitutionsByUserFiscalCode(dummyManager.getTaxCode()); + verify(partyRegistryProxyConnectorMock, times(0)) + .matchInstitutionAndUser(onboardingData.getTaxCode(), dummyManager.getTaxCode()); + verify(onboardingMsConnector, times(0)) + .onboardingCompany(any()); + } + @Test void onboardingPaAggregator() { // given diff --git a/web/src/main/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2Controller.java b/web/src/main/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2Controller.java index 1574c9c1..723fd70b 100644 --- a/web/src/main/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2Controller.java +++ b/web/src/main/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2Controller.java @@ -7,12 +7,15 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import it.pagopa.selfcare.commons.base.logging.LogUtils; +import it.pagopa.selfcare.commons.base.security.SelfCareUser; import it.pagopa.selfcare.commons.web.model.Problem; +import it.pagopa.selfcare.commons.web.security.JwtAuthenticationToken; import it.pagopa.selfcare.onboarding.core.InstitutionService; import it.pagopa.selfcare.onboarding.web.model.*; import it.pagopa.selfcare.onboarding.web.model.mapper.InstitutionMapper; import it.pagopa.selfcare.onboarding.web.model.mapper.OnboardingResourceMapper; import lombok.extern.slf4j.Slf4j; +import org.owasp.encoder.Encode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -20,6 +23,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.validation.Valid; +import java.security.Principal; import java.util.List; import static org.springframework.http.MediaType.APPLICATION_PROBLEM_JSON_VALUE; @@ -71,10 +75,12 @@ public void onboarding(@RequestBody @Valid OnboardingProductDto request) { @PostMapping(value = "/company/onboarding") @ResponseStatus(HttpStatus.CREATED) @ApiOperation(value = "", notes = "${swagger.onboarding.institutions.api.onboarding.subunit}") - public void onboarding(@RequestBody @Valid CompanyOnboardingDto request) { + public void onboarding(@RequestBody @Valid CompanyOnboardingDto request, Principal principal) { log.trace(ONBOARDING_START); - log.debug("onboarding request = {}", request); - institutionService.onboardingCompanyV2(onboardingResourceMapper.toEntity(request)); + log.debug("onboarding request = {}", Encode.forJava(request.toString())); + JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) principal; + SelfCareUser selfCareUser = (SelfCareUser) jwtAuthenticationToken.getPrincipal(); + institutionService.onboardingCompanyV2(onboardingResourceMapper.toEntity(request), selfCareUser.getFiscalCode()); log.trace(ONBOARDING_END); } diff --git a/web/src/test/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2ControllerTest.java b/web/src/test/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2ControllerTest.java index 1886b179..0b775b30 100644 --- a/web/src/test/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2ControllerTest.java +++ b/web/src/test/java/it/pagopa/selfcare/onboarding/web/controller/InstitutionV2ControllerTest.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import it.pagopa.selfcare.commons.base.security.SelfCareUser; import it.pagopa.selfcare.commons.base.utils.ProductId; +import it.pagopa.selfcare.commons.web.security.JwtAuthenticationToken; import it.pagopa.selfcare.onboarding.connector.model.institutions.Institution; import it.pagopa.selfcare.onboarding.connector.model.onboarding.OnboardingData; import it.pagopa.selfcare.onboarding.core.InstitutionService; @@ -12,6 +14,7 @@ import it.pagopa.selfcare.onboarding.web.model.mapper.OnboardingResourceMapperImpl; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; @@ -97,10 +100,16 @@ void onboardingProductForAggregatorAsync(@Value("classpath:stubs/onboardingProdu @Test void onboardingCompany(@Value("classpath:stubs/onboardingCompanyDto.json") Resource onboardingDto) throws Exception { - + //given + JwtAuthenticationToken mockPrincipal = Mockito.mock(JwtAuthenticationToken.class); + SelfCareUser selfCareUser = SelfCareUser.builder("example") + .fiscalCode("fiscalCode") + .build(); + Mockito.when(mockPrincipal.getPrincipal()).thenReturn(selfCareUser); // when mvc.perform(MockMvcRequestBuilders .post(BASE_URL + "/company/onboarding") + .principal(mockPrincipal) .content(onboardingDto.getInputStream().readAllBytes()) .contentType(APPLICATION_JSON_VALUE) .accept(APPLICATION_JSON_VALUE)) @@ -109,7 +118,7 @@ void onboardingCompany(@Value("classpath:stubs/onboardingCompanyDto.json") Resou // then verify(institutionServiceMock, times(1)) - .onboardingCompanyV2(any(OnboardingData.class)); + .onboardingCompanyV2(any(OnboardingData.class), anyString()); verifyNoMoreInteractions(institutionServiceMock); }