diff --git a/api/agencyadminservice.yaml b/api/agencyadminservice.yaml index 237dbf1c..b630ca4e 100644 --- a/api/agencyadminservice.yaml +++ b/api/agencyadminservice.yaml @@ -630,6 +630,9 @@ components: items: type: string enum: [ RELATIVE_COUNSELLING, SELF_COUNSELLING, PARENTAL_COUNSELLING] + dataProtection: + type: object + $ref: '#/components/schemas/DataProtectionDTO' AgencyAdminFullResponseDTO: type: object diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java index 07b9438c..d22fa70a 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java @@ -49,14 +49,13 @@ public class AgencyAdminService { private final @NonNull AgencyTopicMergeService agencyTopicMergeService; private final @NonNull AppointmentService appointmentService; + private final @NonNull DataProtectionConverter dataProtectionConverter; @Autowired(required = false) private AgencyTopicEnrichmentService agencyTopicEnrichmentService; @Autowired(required = false) private DemographicsConverter demographicsConverter; - @Autowired - private DataProtectionConverter dataProtectionConverter; @Value("${feature.topics.enabled}") private boolean featureTopicsEnabled; @@ -147,7 +146,7 @@ private Agency fromAgencyDTO(AgencyDTO agencyDTO) { if (featureDemographicsEnabled && agencyDTO.getDemographics() != null) { demographicsConverter.convertToEntity(agencyDTO.getDemographics(), agencyBuilder); } - + dataProtectionConverter.convertToEntity(agencyDTO.getDataProtection(), agencyBuilder); var agencyToCreate = agencyBuilder.build(); if (featureTopicsEnabled) { @@ -210,9 +209,7 @@ private Agency mergeAgencies(Agency agency, UpdateAgencyDTO updateAgencyDTO) { .counsellingRelations(agency.getCounsellingRelations()) .deleteDate(agency.getDeleteDate()); - if (dataProtectionConverter != null) { - dataProtectionConverter.convertToEntity(updateAgencyDTO.getDataProtection(), agencyBuilder); - } + dataProtectionConverter.convertToEntity(updateAgencyDTO.getDataProtection(), agencyBuilder); if (nonNull(updateAgencyDTO.getConsultingType())) { agencyBuilder.consultingTypeId(updateAgencyDTO.getConsultingType()); diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/AgencyValidator.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/AgencyValidator.java index 35044052..08c869bb 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/AgencyValidator.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/AgencyValidator.java @@ -4,8 +4,10 @@ import de.caritas.cob.agencyservice.api.admin.validation.validators.annotation.CreateAgencyValidator; import de.caritas.cob.agencyservice.api.admin.validation.validators.annotation.UpdateAgencyValidator; import de.caritas.cob.agencyservice.api.admin.validation.validators.model.ValidateAgencyDTO; +import de.caritas.cob.agencyservice.api.exception.httpresponses.BadRequestException; import de.caritas.cob.agencyservice.api.model.AgencyDTO; import de.caritas.cob.agencyservice.api.model.UpdateAgencyDTO; +import de.caritas.cob.agencyservice.api.repository.agency.AgencyRepository; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationContext; @@ -20,6 +22,8 @@ public class AgencyValidator { private final @NonNull ApplicationContext applicationContext; + private final @NonNull AgencyRepository agencyRepository; + /** * Validates an {@link AgencyDTO}. * @@ -53,15 +57,19 @@ private ValidateAgencyDTO fromAgencyDto(AgencyDTO agencyDto) { .postcode(agencyDto.getPostcode()) .consultingType(agencyDto.getConsultingType()) .demographicsDTO(agencyDto.getDemographics()) + .tenantId(agencyDto.getTenantId()) .build(); } private ValidateAgencyDTO fromUpdateAgencyDto(Long agencyId, UpdateAgencyDTO updateAgencyDTO) { + var existingAgency = agencyRepository.findById(agencyId).orElseThrow(() -> new BadRequestException("Agency with id " + agencyId + "not found!")); return ValidateAgencyDTO.builder() .id(agencyId) .postcode(updateAgencyDTO.getPostcode()) .offline(updateAgencyDTO.getOffline()) + .tenantId(existingAgency.getTenantId()) .demographicsDTO(updateAgencyDTO.getDemographics()) + .dataProtectionDTO(updateAgencyDTO.getDataProtection()) .build(); } } diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidationService.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidationService.java new file mode 100644 index 00000000..5e8bd07c --- /dev/null +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidationService.java @@ -0,0 +1,83 @@ +package de.caritas.cob.agencyservice.api.admin.validation.validators; + +import static de.caritas.cob.agencyservice.api.model.DataProtectionDTO.DataProtectionResponsibleEntityEnum.AGENCY_RESPONSIBLE; +import static de.caritas.cob.agencyservice.api.model.DataProtectionDTO.DataProtectionResponsibleEntityEnum.ALTERNATIVE_REPRESENTATIVE; +import static de.caritas.cob.agencyservice.api.model.DataProtectionDTO.DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER; +import static org.apache.commons.lang3.StringUtils.isBlank; + +import de.caritas.cob.agencyservice.api.admin.validation.validators.model.ValidateAgencyDTO; +import de.caritas.cob.agencyservice.api.exception.httpresponses.HttpStatusExceptionReason; +import de.caritas.cob.agencyservice.api.exception.httpresponses.InvalidOfflineStatusException; +import de.caritas.cob.agencyservice.api.model.DataProtectionContactDTO; +import de.caritas.cob.agencyservice.api.repository.agency.DataProtectionPlaceHolderType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class AgencyDataProtectionValidationService { + + public void validate(ValidateAgencyDTO validateAgencyDto) { + validateThatDataProtectionDtoExists(validateAgencyDto); + validateIfDataProtectionOfficer(validateAgencyDto); + validateIfAgencyResponsible(validateAgencyDto); + validateIfAlternativeRepresentative(validateAgencyDto); + } + + private void validateThatDataProtectionDtoExists(ValidateAgencyDTO validateAgencyDto) { + if (validateAgencyDto.getDataProtectionDTO() == null) { + log.warn( + "Could not save agency with id {} status. Required fields for data protection officer is empty.", + validateAgencyDto.getId()); + throw new InvalidOfflineStatusException( + HttpStatusExceptionReason.DATA_PROTECTION_DTO_IS_NULL); + } + } + + private void validateIfDataProtectionOfficer(ValidateAgencyDTO validateAgencyDto) { + if (DATA_PROTECTION_OFFICER.equals( + validateAgencyDto.getDataProtectionDTO().getDataProtectionResponsibleEntity()) + && areFieldsEmpty( + validateAgencyDto.getDataProtectionDTO().getDataProtectionOfficerContact())) { + log.warn( + "Could not save agency with id {}. Required fields for data protection officer is empty.", + validateAgencyDto.getId()); + throw new InvalidOfflineStatusException( + HttpStatusExceptionReason.DATA_PROTECTION_OFFICER_IS_EMPTY); + } + } + + private void validateIfAgencyResponsible(ValidateAgencyDTO validateAgencyDto) { + if (AGENCY_RESPONSIBLE.equals( + validateAgencyDto.getDataProtectionDTO().getDataProtectionResponsibleEntity()) + && areFieldsEmpty( + validateAgencyDto.getDataProtectionDTO().getAgencyDataProtectionResponsibleContact())) { + log.warn( + "Could not save agency with id {} status. Required fields for agency responsible is empty.", + validateAgencyDto.getId()); + throw new InvalidOfflineStatusException( + HttpStatusExceptionReason.DATA_PROTECTION_RESPONSIBLE_IS_EMPTY); + } + } + + private void validateIfAlternativeRepresentative(ValidateAgencyDTO validateAgencyDto) { + if (ALTERNATIVE_REPRESENTATIVE.equals( + validateAgencyDto.getDataProtectionDTO().getDataProtectionResponsibleEntity()) + && areFieldsEmpty(validateAgencyDto.getDataProtectionDTO() + .getAlternativeDataProtectionRepresentativeContact())) { + log.warn( + "Could not save agency with id {} status. Required fields for alternative responsible is empty.", + validateAgencyDto.getId()); + throw new InvalidOfflineStatusException( + HttpStatusExceptionReason.DATA_PROTECTION_ALTERNATIVE_RESPONSIBLE_IS_EMPTY); + } + } + + private boolean areFieldsEmpty(DataProtectionContactDTO dataProtectionOfficerContact) { + return dataProtectionOfficerContact == null + || isBlank(dataProtectionOfficerContact.getNameAndLegalForm()) + || isBlank(dataProtectionOfficerContact.getCity()) + || isBlank(dataProtectionOfficerContact.getPostcode()) + || isBlank(dataProtectionOfficerContact.getEmail()); + } +} diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidator.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidator.java new file mode 100644 index 00000000..634c50d3 --- /dev/null +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidator.java @@ -0,0 +1,48 @@ +package de.caritas.cob.agencyservice.api.admin.validation.validators; + +import de.caritas.cob.agencyservice.api.admin.validation.validators.annotation.UpdateAgencyValidator; +import de.caritas.cob.agencyservice.api.admin.validation.validators.model.ValidateAgencyDTO; +import de.caritas.cob.agencyservice.api.service.ApplicationSettingsService; +import de.caritas.cob.agencyservice.api.service.TenantService; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@UpdateAgencyValidator +@Slf4j +public class AgencyDataProtectionValidator implements ConcreteAgencyValidator { + + private final @NonNull TenantService tenantService; + + private final @NonNull ApplicationSettingsService applicationSettingsService; + + private final @NonNull AgencyDataProtectionValidationService agencyDataProtectionValidationService; + + @Value("${feature.multitenancy.with.single.domain.enabled}") + private boolean multitenancyWithSingleDomain; + + @Override + public void validate(ValidateAgencyDTO validateAgencyDto) { + + var tenant = tenantService.getRestrictedTenantDataByTenantId(validateAgencyDto.getTenantId()); + + if (Boolean.TRUE.equals(tenant.getSettings().getFeatureCentralDataProtectionTemplateEnabled())) { + log.info("Validating agency data protection for agency with id {}.", validateAgencyDto.getId()); + agencyDataProtectionValidationService.validate(validateAgencyDto); + } + + if (multitenancyWithSingleDomain) { + var mainTenantSubdomainForSingleDomainMultitenancy = applicationSettingsService.getApplicationSettings() + .getMainTenantSubdomainForSingleDomainMultitenancy(); + de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO mainTenant = tenantService.getRestrictedTenantDataBySubdomain( + mainTenantSubdomainForSingleDomainMultitenancy.getValue()); + if (Boolean.TRUE.equals(mainTenant.getSettings().getFeatureCentralDataProtectionTemplateEnabled())) { + agencyDataProtectionValidationService.validate(validateAgencyDto); + } + } + } +} diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyOfflineStatusValidator.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyOfflineStatusValidator.java index 9f044d52..36595aec 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyOfflineStatusValidator.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyOfflineStatusValidator.java @@ -16,6 +16,7 @@ import de.caritas.cob.agencyservice.consultingtypeservice.generated.web.model.ExtendedConsultingTypeResponseDTO; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /* @@ -24,6 +25,7 @@ @Component @RequiredArgsConstructor @UpdateAgencyValidator +@Slf4j public class AgencyOfflineStatusValidator implements ConcreteAgencyValidator { private final @NonNull AgencyRepository agencyRepository; @@ -48,7 +50,6 @@ public void validate(ValidateAgencyDTO validateAgencyDto) { if (hasNoConsultant(validateAgencyDto)) { throw new InvalidOfflineStatusException(AGENCY_CONTAINS_NO_CONSULTANTS); } - } } diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/model/ValidateAgencyDTO.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/model/ValidateAgencyDTO.java index 81a66182..af30d670 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/model/ValidateAgencyDTO.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/validation/validators/model/ValidateAgencyDTO.java @@ -1,5 +1,6 @@ package de.caritas.cob.agencyservice.api.admin.validation.validators.model; +import de.caritas.cob.agencyservice.api.model.DataProtectionDTO; import de.caritas.cob.agencyservice.api.model.DemographicsDTO; import lombok.Builder; import lombok.Getter; @@ -18,6 +19,9 @@ public class ValidateAgencyDTO { private String postcode; private Integer consultingType; private Boolean offline; + private Long tenantId; private DemographicsDTO demographicsDTO; + private DataProtectionDTO dataProtectionDTO; + } diff --git a/src/main/java/de/caritas/cob/agencyservice/api/exception/httpresponses/HttpStatusExceptionReason.java b/src/main/java/de/caritas/cob/agencyservice/api/exception/httpresponses/HttpStatusExceptionReason.java index 5aaa9d77..82479849 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/exception/httpresponses/HttpStatusExceptionReason.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/exception/httpresponses/HttpStatusExceptionReason.java @@ -11,5 +11,12 @@ public enum HttpStatusExceptionReason { AGENCY_CONTAINS_NO_CONSULTANTS, AGENCY_IS_ALREADY_TEAM_AGENCY, AGENCY_IS_ALREADY_DEFAULT_AGENCY, + DATA_PROTECTION_OFFICER_IS_EMPTY, + DATA_PROTECTION_RESPONSIBLE_IS_EMPTY, + + DATA_PROTECTION_ALTERNATIVE_RESPONSIBLE_IS_EMPTY, + + DATA_PROTECTION_DTO_IS_NULL, + AGENCY_ACCESS_DENIED } diff --git a/src/main/java/de/caritas/cob/agencyservice/api/repository/agency/DataProtectionPlaceHolderType.java b/src/main/java/de/caritas/cob/agencyservice/api/repository/agency/DataProtectionPlaceHolderType.java new file mode 100644 index 00000000..1b313ba7 --- /dev/null +++ b/src/main/java/de/caritas/cob/agencyservice/api/repository/agency/DataProtectionPlaceHolderType.java @@ -0,0 +1,28 @@ +package de.caritas.cob.agencyservice.api.repository.agency; + +import lombok.Getter; + +@Getter +public enum DataProtectionPlaceHolderType { + DATA_PROTECTION_RESPONSIBLE("responsible"), + DATA_PROTECTION_OFFICER("dataProtectionOfficer"); + + private final String placeholderVariable; + + DataProtectionPlaceHolderType(String placeholderVariable) { + this.placeholderVariable = placeholderVariable; + } + + public static boolean anyExistsInText(String privacy) { + return DATA_PROTECTION_RESPONSIBLE.existsInText(privacy) + || DATA_PROTECTION_OFFICER.existsInText(privacy); + } + + public boolean existsInText(String text) { + return text.contains(getPlaceholder()); + } + + public String getPlaceholder() { + return "${" + placeholderVariable + "}"; + } +} diff --git a/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceTest.java b/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceTest.java index edf30c75..0ef74ef9 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceTest.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceTest.java @@ -26,15 +26,14 @@ import de.caritas.cob.agencyservice.api.model.AgencyAdminResponseDTO; import de.caritas.cob.agencyservice.api.model.AgencyTypeRequestDTO; import de.caritas.cob.agencyservice.api.model.DataProtectionContactDTO; +import de.caritas.cob.agencyservice.api.model.DataProtectionDTO; import de.caritas.cob.agencyservice.api.model.DemographicsDTO; import de.caritas.cob.agencyservice.api.model.UpdateAgencyDTO; import de.caritas.cob.agencyservice.api.model.AgencyDTO; import de.caritas.cob.agencyservice.api.repository.agency.Agency; -import de.caritas.cob.agencyservice.api.repository.agency.AgencyRepository; import de.caritas.cob.agencyservice.api.repository.agency.AgencyTenantUnawareRepository; import de.caritas.cob.agencyservice.api.repository.agency.DataProtectionResponsibleEntity; import de.caritas.cob.agencyservice.api.service.AppointmentService; -import de.caritas.cob.agencyservice.api.service.LogService; import de.caritas.cob.agencyservice.api.util.JsonConverter; import java.util.List; import java.util.Optional; @@ -108,6 +107,7 @@ void updateAgency_Should_ThrowNotFoundException_WhenAgencyIsNotFound() { @Test void createAgency_Should_CreateAgencyAndAddDefaultCounsellingRelations() { + // given var agency = this.easyRandom.nextObject(Agency.class); agency.setCounsellingRelations(null); agency.setDataProtectionOfficerContactData(null); @@ -115,11 +115,15 @@ void createAgency_Should_CreateAgencyAndAddDefaultCounsellingRelations() { var agencyDTO = this.easyRandom.nextObject(AgencyDTO.class); agencyDTO.setCounsellingRelations(null); agencyDTO.setConsultingType(1); + agencyDTO.setDataProtection(new DataProtectionDTO()); when(agencyRepository.save(any())).thenReturn(agency); + // when agencyAdminService.createAgency(agencyDTO); + // then verify(agencyRepository).save(agencyArgumentCaptor.capture()); assertThat(agencyArgumentCaptor.getValue().getCounsellingRelations(), is("RELATIVE_COUNSELLING,SELF_COUNSELLING,PARENTAL_COUNSELLING")); + verify(dataProtectionConverter).convertToEntity(Mockito.any(DataProtectionDTO.class), Mockito.any(Agency.AgencyBuilder.class)); } @Test diff --git a/src/test/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidationServiceTest.java b/src/test/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidationServiceTest.java new file mode 100644 index 00000000..48dced04 --- /dev/null +++ b/src/test/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidationServiceTest.java @@ -0,0 +1,133 @@ +package de.caritas.cob.agencyservice.api.admin.validation.validators; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import de.caritas.cob.agencyservice.api.admin.validation.validators.model.ValidateAgencyDTO; +import de.caritas.cob.agencyservice.api.exception.httpresponses.HttpStatusExceptionReason; +import de.caritas.cob.agencyservice.api.exception.httpresponses.InvalidOfflineStatusException; +import de.caritas.cob.agencyservice.api.model.DataProtectionContactDTO; +import de.caritas.cob.agencyservice.api.model.DataProtectionDTO; +import de.caritas.cob.agencyservice.api.model.DataProtectionDTO.DataProtectionResponsibleEntityEnum; +import de.caritas.cob.agencyservice.tenantservice.generated.web.model.Content; +import de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO; +import org.assertj.core.api.Fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AgencyDataProtectionValidationServiceTest { + + @InjectMocks + AgencyDataProtectionValidationService agencyDataProtectionValidator; + + @Test + void validate_Should_NotValidate_When_DataProtectionOfficerIsNotSet() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER)).build(); + + // when + try { + agencyDataProtectionValidator.validate( + agencyToValidate); + } catch (InvalidOfflineStatusException e) { + // then + assertThat(e.getHttpStatusExceptionReason()).isEqualTo(HttpStatusExceptionReason.DATA_PROTECTION_OFFICER_IS_EMPTY); + } + } + + private de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO tenantWithPrivacy( + String privacy) { + return new RestrictedTenantDTO().content(new Content().privacy(privacy)); + } + + @Test + void validate_Should_Validate_When_DataProtectionOfficerIsSet() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity(DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER) + .dataProtectionOfficerContact(new DataProtectionContactDTO().nameAndLegalForm("name").email("email").city("city").postcode("postcode"))).build(); + // when + try { + agencyDataProtectionValidator.validate( + agencyToValidate); + } catch (Exception e) { + // then + Fail.fail("Should not throw exception"); + } + } + + @Test + void validate_Should_Validate_When_DataResponsibleIsSet() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.AGENCY_RESPONSIBLE).agencyDataProtectionResponsibleContact(new DataProtectionContactDTO().nameAndLegalForm("name").email("email").city("city").postcode("postcode")) + ).build(); + // when + try { + agencyDataProtectionValidator.validate( + agencyToValidate); + } catch (Exception e) { + // then + Fail.fail("Should not throw exception"); + } + } + + + @Test + void validate_Should_NotValidate_When_DataResponsibleIsNotSet() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.AGENCY_RESPONSIBLE) + ).build(); + // when + try { + agencyDataProtectionValidator.validate( + agencyToValidate); + } catch (InvalidOfflineStatusException e) { + // then + assertThat(e.getHttpStatusExceptionReason()).isEqualTo(HttpStatusExceptionReason.DATA_PROTECTION_RESPONSIBLE_IS_EMPTY); + } + } + + @Test + void validate_Should_NotValidate_When_AlternativeDataResponsibleIsNotSet() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.ALTERNATIVE_REPRESENTATIVE)).build(); + // when + try { + agencyDataProtectionValidator.validate(agencyToValidate); + } catch (InvalidOfflineStatusException e) { + // then + assertThat(e.getHttpStatusExceptionReason()).isEqualTo(HttpStatusExceptionReason.DATA_PROTECTION_ALTERNATIVE_RESPONSIBLE_IS_EMPTY); + } + } + + + @Test + void validate_Should_Validate_When_AlternativeDataResponsibleIsSet() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.ALTERNATIVE_REPRESENTATIVE) + .alternativeDataProtectionRepresentativeContact( + new DataProtectionContactDTO().nameAndLegalForm("name").city("city") + .postcode("postcode").email("email"))).build(); + + // when + try { + agencyDataProtectionValidator.validate( + agencyToValidate); + } catch (Exception e) { + // then + Fail.fail("Should not throw exception"); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidatorTest.java b/src/test/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidatorTest.java new file mode 100644 index 00000000..29ef1967 --- /dev/null +++ b/src/test/java/de/caritas/cob/agencyservice/api/admin/validation/validators/AgencyDataProtectionValidatorTest.java @@ -0,0 +1,109 @@ +package de.caritas.cob.agencyservice.api.admin.validation.validators; + +import de.caritas.cob.agencyservice.api.admin.validation.validators.model.ValidateAgencyDTO; +import de.caritas.cob.agencyservice.api.model.DataProtectionDTO; +import de.caritas.cob.agencyservice.api.model.DataProtectionDTO.DataProtectionResponsibleEntityEnum; +import de.caritas.cob.agencyservice.api.service.ApplicationSettingsService; +import de.caritas.cob.agencyservice.api.service.TenantService; +import de.caritas.cob.agencyservice.applicationsettingsservice.generated.web.model.ApplicationSettingsDTO; +import de.caritas.cob.agencyservice.applicationsettingsservice.generated.web.model.ApplicationSettingsDTOMainTenantSubdomainForSingleDomainMultitenancy; +import de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO; +import de.caritas.cob.agencyservice.tenantservice.generated.web.model.Settings; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +@ExtendWith(MockitoExtension.class) +class AgencyDataProtectionValidatorTest { + + @InjectMocks + AgencyDataProtectionValidator agencyDataProtectionValidator; + + private @Mock AgencyDataProtectionValidationService agencyDataProtectionValidationService; + + private @Mock TenantService tenantService; + + private @Mock ApplicationSettingsService applicationSettingsService; + + @Test + void validate_Should_ValidateForAgencyTenant_When_NonSingleDomainMultitenancy_And_CentralDataProtectionFeatureEnabled() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER)).build(); + ReflectionTestUtils.setField(agencyDataProtectionValidator, "multitenancyWithSingleDomain", + false); + givenAgencyTenant(agencyToValidate, true); + // when + agencyDataProtectionValidator.validate(agencyToValidate); + // then + Mockito.verify(agencyDataProtectionValidationService, Mockito.times(1)) + .validate(agencyToValidate); + } + + @Test + void validate_Should_NotValidateForAgencyTenant_When_NonSingleDomainMultitenancy_And_CentralDataProtectionFeatureDisabled() { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER)).build(); + ReflectionTestUtils.setField(agencyDataProtectionValidator, "multitenancyWithSingleDomain", + false); + givenAgencyTenant(agencyToValidate, false); + // when + agencyDataProtectionValidator.validate(agencyToValidate); + // then + Mockito.verify(agencyDataProtectionValidationService, Mockito.never()) + .validate(agencyToValidate); + } + + @ParameterizedTest + @CsvSource({ + "true,true,2", + "true,false,1", + "false,true,1", + "false,false,0", + }) + void validate_Should_ValidateForAgencyTenantAndMainTenant_When_SingleDomainMultitenancy(boolean isAgencyTenantCentralDataProtectionEnabled, boolean isMainTenantCentralDataProtectionEnabled, int expectedValidationCalls) { + // given + ValidateAgencyDTO agencyToValidate = ValidateAgencyDTO.builder() + .dataProtectionDTO(new DataProtectionDTO().dataProtectionResponsibleEntity( + DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER)).build(); + ReflectionTestUtils.setField(agencyDataProtectionValidator, "multitenancyWithSingleDomain", + true); + givenAgencyTenant(agencyToValidate, isAgencyTenantCentralDataProtectionEnabled); + givenSingleDomainWithValue("app"); + givenMainTenant("app", isMainTenantCentralDataProtectionEnabled); + // when + agencyDataProtectionValidator.validate(agencyToValidate); + // then + + Mockito.verify(agencyDataProtectionValidationService, Mockito.times(expectedValidationCalls)) + .validate(agencyToValidate); + } + + + + private void givenAgencyTenant(ValidateAgencyDTO agency, boolean isCentralDataProtectionEnabled) { + Mockito.when(tenantService.getRestrictedTenantDataByTenantId(agency.getTenantId())) + .thenReturn(new RestrictedTenantDTO().settings(new Settings().featureCentralDataProtectionTemplateEnabled(isCentralDataProtectionEnabled))); + } + + private void givenMainTenant(String domain, boolean isCentralDataProtectionEnabled) { + Mockito.when(tenantService.getRestrictedTenantDataBySubdomain(domain)) + .thenReturn(new RestrictedTenantDTO().settings(new Settings().featureCentralDataProtectionTemplateEnabled(isCentralDataProtectionEnabled))); + } + + private void givenSingleDomainWithValue(String domain) { + Mockito.when(applicationSettingsService.getApplicationSettings()).thenReturn( + new ApplicationSettingsDTO().mainTenantSubdomainForSingleDomainMultitenancy( + new ApplicationSettingsDTOMainTenantSubdomainForSingleDomainMultitenancy().value( + domain))); + } +} \ No newline at end of file diff --git a/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerIT.java b/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerIT.java index f5005e2b..e57be60f 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerIT.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerIT.java @@ -3,6 +3,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.when; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @@ -16,6 +17,7 @@ import de.caritas.cob.agencyservice.api.admin.service.UserAdminService; import de.caritas.cob.agencyservice.api.model.DataProtectionContactDTO; import de.caritas.cob.agencyservice.api.model.DataProtectionDTO; +import de.caritas.cob.agencyservice.api.service.TenantService; import de.caritas.cob.agencyservice.api.util.AuthenticatedUser; import de.caritas.cob.agencyservice.api.manager.consultingtype.ConsultingTypeManager; import de.caritas.cob.agencyservice.api.model.AgencyDTO; @@ -24,10 +26,12 @@ import de.caritas.cob.agencyservice.api.tenant.TenantContext; import de.caritas.cob.agencyservice.consultingtypeservice.generated.web.model.ExtendedConsultingTypeResponseDTO; import de.caritas.cob.agencyservice.api.util.JsonConverter; +import de.caritas.cob.agencyservice.tenantservice.generated.web.model.Settings; import de.caritas.cob.agencyservice.testHelper.PathConstants; import org.jeasy.random.EasyRandom; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -68,6 +72,9 @@ class AgencyAdminControllerIT { @Autowired private AgencyRepository agencyRepository; + @MockBean + private TenantService tenantService; + @BeforeEach public void setup() { TenantContext.clear(); @@ -75,6 +82,8 @@ public void setup() { .webAppContextSetup(context) .apply(springSecurity()) .build(); + when(tenantService.getRestrictedTenantDataByTenantId(Mockito.any())) + .thenReturn(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO().settings(new Settings().featureCentralDataProtectionTemplateEnabled(false))); } @Test @@ -245,12 +254,79 @@ void updateAgency_Should_returnStatusOk_When_calledWithEmptyDescription() throws assertNull(savedAgency.getDescription()); } + @Test + @WithMockUser(authorities = "AUTHORIZATION_AGENCY_ADMIN") + void updateAgency_Should_returnStatusOk_When_CentralDataProtectionIsEnabled_And_PayloadContainsValidDataProtectionContent() throws Exception { + var response = new ExtendedConsultingTypeResponseDTO(); + + Long tenantId = agencyRepository.findById(1L).get().getTenantId(); + when(consultingTypeManager.getConsultingTypeSettings(anyInt())).thenReturn(response); + + when(tenantService.getRestrictedTenantDataByTenantId(tenantId)) + .thenReturn(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO().settings(new Settings().featureCentralDataProtectionTemplateEnabled(true))); + + var agencyDTO = new UpdateAgencyDTO() + .name("Test update name") + .description(null) + .offline(true) + .external(false) + .dataProtection(new DataProtectionDTO().dataProtectionResponsibleEntity(DataProtectionDTO.DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER) + .dataProtectionOfficerContact(new DataProtectionContactDTO().nameAndLegalForm("data protection contact").city("Munich") + .postcode("00001").phoneNumber("321-321-321").email("dataprotection@onlineberatung.net"))); + + + mockMvc.perform(put(PathConstants.UPDATE_DELETE_AGENCY_PATH) + .contentType(APPLICATION_JSON) + .content(JsonConverter.convertToJson(agencyDTO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("_embedded.id").value(1)) + .andExpect(jsonPath("_embedded.name").value("Test update name")) + .andExpect(jsonPath("_embedded.description").isEmpty()) + .andExpect(jsonPath("_embedded.teamAgency").value("false")) + .andExpect(jsonPath("_embedded.external").value("false")) + .andExpect(jsonPath("_embedded.offline").exists()) + .andExpect(jsonPath("_embedded.topics").exists()) + .andExpect(jsonPath("_embedded.createDate").exists()) + .andExpect(jsonPath("_embedded.updateDate").exists()) + .andExpect(jsonPath("_embedded.deleteDate").exists()); + + var savedAgency = agencyRepository.findById(1L).orElseThrow(); + assertNull(savedAgency.getDescription()); + } + + @Test + @WithMockUser(authorities = "AUTHORIZATION_AGENCY_ADMIN") + void updateAgency_Should_returnStatusBadRequest_When_CentralDataProtectionIsEnabled_And_PayloadContainsInvalidDataProtectionContent() throws Exception { + var response = new ExtendedConsultingTypeResponseDTO(); + + Long tenantId = agencyRepository.findById(1L).get().getTenantId(); + when(consultingTypeManager.getConsultingTypeSettings(anyInt())).thenReturn(response); + + when(tenantService.getRestrictedTenantDataByTenantId(tenantId)) + .thenReturn(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO().settings(new Settings().featureCentralDataProtectionTemplateEnabled(true))); + + var agencyDTO = new UpdateAgencyDTO() + .name("Test update name") + .description(null) + .offline(true) + .external(false) + .dataProtection(new DataProtectionDTO().dataProtectionResponsibleEntity(DataProtectionDTO.DataProtectionResponsibleEntityEnum.DATA_PROTECTION_OFFICER) + .dataProtectionOfficerContact(new DataProtectionContactDTO())); + + + mockMvc.perform(put(PathConstants.UPDATE_DELETE_AGENCY_PATH) + .contentType(APPLICATION_JSON) + .content(JsonConverter.convertToJson(agencyDTO))) + .andExpect(status().isBadRequest()); + } + @Test @WithMockUser(authorities = "AUTHORIZATION_AGENCY_ADMIN") void updateAgency_Should_persistDataProtectionAttributes_When_payloadContainsDataProtection() throws Exception { var response = new ExtendedConsultingTypeResponseDTO(); when(consultingTypeManager.getConsultingTypeSettings(anyInt())).thenReturn(response); + var agencyDTO = new UpdateAgencyDTO() .name("Test update name") .description(null) diff --git a/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithDemographicsIT.java b/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithDemographicsIT.java index 85ca391e..489b4f90 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithDemographicsIT.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithDemographicsIT.java @@ -12,6 +12,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import de.caritas.cob.agencyservice.api.service.TenantService; import de.caritas.cob.agencyservice.api.util.AuthenticatedUser; import de.caritas.cob.agencyservice.api.manager.consultingtype.ConsultingTypeManager; import de.caritas.cob.agencyservice.api.model.AgencyDTO; @@ -25,6 +26,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -58,6 +60,9 @@ class AgencyAdminControllerWithDemographicsIT { @MockBean AuthenticatedUser authenticatedUser; + @MockBean + TenantService tenantService; + @BeforeEach public void setup() { TenantContext.clear(); @@ -65,6 +70,9 @@ public void setup() { .webAppContextSetup(context) .apply(springSecurity()) .build(); + when(tenantService.getRestrictedTenantDataByTenantId(Mockito.any())) + .thenReturn(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO().settings(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.Settings().featureCentralDataProtectionTemplateEnabled(false))); + } @Test diff --git a/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithTopicsIT.java b/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithTopicsIT.java index e7daf7b7..6d47367b 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithTopicsIT.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/controller/AgencyAdminControllerWithTopicsIT.java @@ -11,6 +11,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import de.caritas.cob.agencyservice.api.service.TenantService; import de.caritas.cob.agencyservice.api.util.AuthenticatedUser; import de.caritas.cob.agencyservice.api.manager.consultingtype.ConsultingTypeManager; import de.caritas.cob.agencyservice.api.model.AgencyDTO; @@ -23,6 +24,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -59,6 +61,9 @@ class AgencyAdminControllerWithTopicsIT { @MockBean private AuthenticatedUser authenticatedUser; + @MockBean + private TenantService tenantService; + @BeforeEach public void setup() { TenantContext.clear(); @@ -66,6 +71,8 @@ public void setup() { .webAppContextSetup(context) .apply(springSecurity()) .build(); + when(tenantService.getRestrictedTenantDataByTenantId(Mockito.any())) + .thenReturn(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO().settings(new de.caritas.cob.agencyservice.tenantservice.generated.web.model.Settings().featureCentralDataProtectionTemplateEnabled(false))); } @Test