diff --git a/src/main/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateService.java b/src/main/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateService.java index c688d067..f685b7ee 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateService.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateService.java @@ -15,6 +15,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO; @@ -27,9 +28,23 @@ public class CentralDataProtectionTemplateService { private final @NonNull TemplateRenderer templateRenderer; + private final @NonNull ApplicationSettingsService applicationSettingsService; + + @Value("${feature.multitenancy.with.single.domain.enabled}") + private boolean multitenancyWithSingleDomain; + + private boolean isTenantLevelLegalContentOverrideAllowed() { + de.caritas.cob.agencyservice.applicationsettingsservice.generated.web.model.ApplicationSettingsDTOMultitenancyWithSingleDomainEnabled + legalContentChangesBySingleTenantAdminsAllowed = + applicationSettingsService + .getApplicationSettings() + .getLegalContentChangesBySingleTenantAdminsAllowed(); + return legalContentChangesBySingleTenantAdminsAllowed != null + && Boolean.TRUE.equals(legalContentChangesBySingleTenantAdminsAllowed.getValue()); + } + public String renderPrivacyTemplateWithRenderedPlaceholderValues(Agency agency) { - RestrictedTenantDTO restrictedTenantDataByTenantId = tenantService.getRestrictedTenantDataByTenantId( - agency.getTenantId()); + RestrictedTenantDTO restrictedTenantDataByTenantId = retrieveProperTenant(agency); if (restrictedTenantDataByTenantId != null && restrictedTenantDataByTenantId.getContent() != null) { return renderPrivacyTemplateWithRenderedPlaceholderValues(agency, @@ -42,7 +57,8 @@ public String renderPrivacyTemplateWithRenderedPlaceholderValues(Agency agency) @Nullable private String renderPrivacyTemplateWithRenderedPlaceholderValues(Agency agency, RestrictedTenantDTO restrictedTenantDataByTenantId) { - var renderedPlaceholdersMap = renderDataProtectionPlaceholdersFromTemplates(agency); + var renderedPlaceholdersMap = renderDataProtectionPlaceholdersFromTemplates(agency, + restrictedTenantDataByTenantId); Map dataModel = renderedPlaceholdersMap.entrySet().stream() .collect(Collectors.toMap(entry -> entry.getKey().getPlaceholderVariable(), Entry::getValue)); @@ -56,10 +72,26 @@ private String renderPrivacyTemplateWithRenderedPlaceholderValues(Agency agency, } } - protected Map renderDataProtectionPlaceholdersFromTemplates( + private de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO retrieveProperTenant( + Agency agency) { + if (multitenancyWithSingleDomain) { + return getAgencyTenantOrFallbackToMainTenantIfTenantPrivacyOverrideNotAllowed(agency); + } else { + return tenantService.getRestrictedTenantDataByTenantId(agency.getTenantId()); + } + } + + private de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO getAgencyTenantOrFallbackToMainTenantIfTenantPrivacyOverrideNotAllowed( Agency agency) { - RestrictedTenantDTO restrictedTenantDataByTenantId = tenantService.getRestrictedTenantDataByTenantId( - agency.getTenantId()); + if (isTenantLevelLegalContentOverrideAllowed()) { + return tenantService.getRestrictedTenantDataByTenantId(agency.getTenantId()); + } else { + return tenantService.getMainTenant(); + } + } + + protected Map renderDataProtectionPlaceholdersFromTemplates( + Agency agency, RestrictedTenantDTO restrictedTenantDataByTenantId) { Map result = Maps.newHashMap(); if (restrictedTenantDataByTenantId.getContent() != null @@ -67,15 +99,16 @@ protected Map renderDataProtectionPlaceho var renderedDataProtectionOfficerContact = renderDataProtectionOfficerContactFromTemplate( agency, restrictedTenantDataByTenantId.getContent().getDataProtectionContactTemplate()); - result.put(DataProtectionPlaceHolderType.DATA_PROTECTION_OFFICER, - renderedDataProtectionOfficerContact != null ? renderedDataProtectionOfficerContact : StringUtils.EMPTY); + renderedDataProtectionOfficerContact != null ? renderedDataProtectionOfficerContact + : StringUtils.EMPTY); var renderedDataProtectionResponsible = renderDataProtectionResponsibleFromTemplate( agency, restrictedTenantDataByTenantId.getContent().getDataProtectionContactTemplate()); result.put(DataProtectionPlaceHolderType.DATA_PROTECTION_RESPONSIBLE, - renderedDataProtectionResponsible != null ? renderedDataProtectionResponsible : StringUtils.EMPTY); + renderedDataProtectionResponsible != null ? renderedDataProtectionResponsible + : StringUtils.EMPTY); } return result; diff --git a/src/main/java/de/caritas/cob/agencyservice/api/service/TenantService.java b/src/main/java/de/caritas/cob/agencyservice/api/service/TenantService.java index 0e5f2525..0f3a9b9a 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/service/TenantService.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/service/TenantService.java @@ -16,9 +16,15 @@ public class TenantService { private final @NonNull TenantServiceApiControllerFactory tenantServiceApiControllerFactory; + private final @NonNull ApplicationSettingsService applicationSettingsService; + @Value("${multitenancy.enabled}") private boolean multitenancy; + + @Value("${feature.multitenancy.with.single.domain.enabled}") + private boolean multitenancyWithSingleDomain; + @Cacheable(cacheNames = CacheManagerConfig.TENANT_CACHE, key = "#subdomain") public RestrictedTenantDTO getRestrictedTenantDataBySubdomain(String subdomain) { TenantControllerApi controllerApi = tenantServiceApiControllerFactory.createControllerApi(); @@ -34,4 +40,17 @@ public RestrictedTenantDTO getRestrictedTenantDataForSingleTenant() { TenantControllerApi controllerApi = tenantServiceApiControllerFactory.createControllerApi(); return controllerApi.getRestrictedSingleTenancyTenantData(); } + + public RestrictedTenantDTO getMainTenant() { + if (multitenancyWithSingleDomain) { + return getRestrictedTenantDataBySubdomain(getMainTenantSubdomain()); + } + throw new IllegalStateException("Main tenant can only be retrieved if multitenancy with single domain is enabled."); + } + + private String getMainTenantSubdomain() { + return applicationSettingsService + .getApplicationSettings() + .getMainTenantSubdomainForSingleDomainMultitenancy().getValue(); + } } diff --git a/src/test/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateServiceTest.java b/src/test/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateServiceTest.java index fc4c5511..b1ce330c 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateServiceTest.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/service/CentralDataProtectionTemplateServiceTest.java @@ -8,11 +8,14 @@ import de.caritas.cob.agencyservice.api.repository.agency.Agency; import de.caritas.cob.agencyservice.api.repository.agency.DataProtectionResponsibleEntity; import de.caritas.cob.agencyservice.api.util.JsonConverter; +import de.caritas.cob.agencyservice.applicationsettingsservice.generated.web.model.ApplicationSettingsDTO; +import de.caritas.cob.agencyservice.applicationsettingsservice.generated.web.model.ApplicationSettingsDTOMultitenancyWithSingleDomainEnabled; import de.caritas.cob.agencyservice.tenantservice.generated.web.model.AgencyContextDTO; import de.caritas.cob.agencyservice.tenantservice.generated.web.model.Content; import de.caritas.cob.agencyservice.tenantservice.generated.web.model.DataProtectionContactTemplateDTO; import de.caritas.cob.agencyservice.tenantservice.generated.web.model.DataProtectionOfficerDTO; import de.caritas.cob.agencyservice.tenantservice.generated.web.model.RestrictedTenantDTO; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +25,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; @RunWith(SpringRunner.class) @SpringBootTest @@ -37,6 +41,18 @@ class CentralDataProtectionTemplateServiceTest { @MockBean TenantService tenantService; + @MockBean + ApplicationSettingsService applicationSettingsService; + + @BeforeEach + void setup() { + ReflectionTestUtils.setField(centralDataProtectionTemplateService, + "multitenancyWithSingleDomain", false); + when(applicationSettingsService.getApplicationSettings()).thenReturn( + new ApplicationSettingsDTO().legalContentChangesBySingleTenantAdminsAllowed( + new ApplicationSettingsDTOMultitenancyWithSingleDomainEnabled().value(true))); + } + @Test void renderDataProtectionPrivacy_shouldProperlyRenderPrivacy_When_PlaceholdersAreRendered() { @@ -57,7 +73,8 @@ void renderDataProtectionPrivacy_shouldProperlyRenderPrivacy_When_PlaceholdersAr .name("agencyName") .dataProtectionResponsibleEntity(DataProtectionResponsibleEntity.DATA_PROTECTION_OFFICER) .dataProtectionOfficerContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) - .dataProtectionAgencyResponsibleContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) + .dataProtectionAgencyResponsibleContactData( + JsonConverter.convertToJson(dataProtectionContactDTO)) .build(); // when @@ -70,6 +87,47 @@ void renderDataProtectionPrivacy_shouldProperlyRenderPrivacy_When_PlaceholdersAr "Privacy template with placeholders: Data protection officer contact name: Max Mustermann, Data protection responsible contact name: Max Mustermann,"); } + @Test + void renderDataProtectionPrivacy_shouldProperlyRenderPrivacyTakingTemplateFromMainTenant_When_MultitenancySingleDomainAndTenantLevelPrivacyOverrideNotAllowed() { + // given + ReflectionTestUtils.setField(centralDataProtectionTemplateService, + "multitenancyWithSingleDomain", true); + when(applicationSettingsService.getApplicationSettings()).thenReturn( + new ApplicationSettingsDTO().legalContentChangesBySingleTenantAdminsAllowed( + new ApplicationSettingsDTOMultitenancyWithSingleDomainEnabled().value(false))); + + when(tenantService.getMainTenant()).thenReturn( + new RestrictedTenantDTO() + .content( + new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate()) + .privacy( + "Privacy template with placeholders from main tenant: ${dataProtectionOfficer} ${responsible}"))); + DataProtectionContactDTO dataProtectionContactDTO = new DataProtectionContactDTO() + .nameAndLegalForm("Max Mustermann"); + + Agency agency = Agency.builder() + .id(1000L) + .tenantId(1L) + .consultingTypeId(1) + .name("agencyName") + .dataProtectionResponsibleEntity(DataProtectionResponsibleEntity.DATA_PROTECTION_OFFICER) + .dataProtectionOfficerContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) + .dataProtectionAgencyResponsibleContactData( + JsonConverter.convertToJson(dataProtectionContactDTO)) + .build(); + + // when + var renderedPrivacy = centralDataProtectionTemplateService.renderPrivacyTemplateWithRenderedPlaceholderValues( + agency); + + // then + assertThat( + renderedPrivacy).isEqualTo( + "Privacy template with placeholders from main tenant: Data protection officer contact name: Max Mustermann, Data protection responsible contact name: Max Mustermann,"); + + } + + @Test void renderDataProtectionPrivacy_shouldReturnPrivacyAsItIs_When_PlaceholdersAreNotIncludedInPrivacy() { @@ -90,7 +148,8 @@ void renderDataProtectionPrivacy_shouldReturnPrivacyAsItIs_When_PlaceholdersAreN .name("agencyName") .dataProtectionResponsibleEntity(DataProtectionResponsibleEntity.DATA_PROTECTION_OFFICER) .dataProtectionOfficerContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) - .dataProtectionAgencyResponsibleContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) + .dataProtectionAgencyResponsibleContactData( + JsonConverter.convertToJson(dataProtectionContactDTO)) .build(); // when @@ -107,10 +166,11 @@ void renderDataProtectionPrivacy_shouldReturnPrivacyAsItIs_When_PlaceholdersAreN void renderDataProtectionTemplatePlaceholders_shouldProperlyRenderPlaceholders_If_SomeVariableDataIsMissing() { // given + RestrictedTenantDTO tenantDTO = new RestrictedTenantDTO() + .content( + new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate())); when(tenantService.getRestrictedTenantDataByTenantId(anyLong())).thenReturn( - new RestrictedTenantDTO() - .content( - new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate()))); + tenantDTO); DataProtectionContactDTO dataProtectionContactDTO = new DataProtectionContactDTO() .nameAndLegalForm("Max Mustermann"); @@ -121,12 +181,13 @@ void renderDataProtectionTemplatePlaceholders_shouldProperlyRenderPlaceholders_I .name("agencyName") .dataProtectionResponsibleEntity(DataProtectionResponsibleEntity.DATA_PROTECTION_OFFICER) .dataProtectionOfficerContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) - .dataProtectionAgencyResponsibleContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) + .dataProtectionAgencyResponsibleContactData( + JsonConverter.convertToJson(dataProtectionContactDTO)) .build(); // when var renderedPlaceholders = centralDataProtectionTemplateService.renderDataProtectionPlaceholdersFromTemplates( - agency); + agency, tenantDTO); // then assertThat( @@ -140,10 +201,11 @@ void renderDataProtectionTemplatePlaceholders_shouldProperlyRenderPlaceholders_I void renderDataProtectionTemplatePlaceholders_shouldProperlyRenderPlaceholders() { // given + RestrictedTenantDTO tenantDTO = new RestrictedTenantDTO() + .content( + new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate())); when(tenantService.getRestrictedTenantDataByTenantId(anyLong())).thenReturn( - new RestrictedTenantDTO() - .content( - new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate()))); + tenantDTO); DataProtectionContactDTO dataProtectionContactDTO = new DataProtectionContactDTO() .nameAndLegalForm("Max Mustermann") .street("Musterstraße 1") @@ -158,12 +220,13 @@ void renderDataProtectionTemplatePlaceholders_shouldProperlyRenderPlaceholders() .name("agencyName") .dataProtectionResponsibleEntity(DataProtectionResponsibleEntity.DATA_PROTECTION_OFFICER) .dataProtectionOfficerContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) - .dataProtectionAgencyResponsibleContactData(JsonConverter.convertToJson(dataProtectionContactDTO)) + .dataProtectionAgencyResponsibleContactData( + JsonConverter.convertToJson(dataProtectionContactDTO)) .build(); // when var renderedPlaceholders = centralDataProtectionTemplateService.renderDataProtectionPlaceholdersFromTemplates( - agency); + agency, tenantDTO); // then assertThat( @@ -178,10 +241,11 @@ void renderDataProtectionTemplatePlaceholders_shouldProperlyRenderPlaceholders() void renderDataProtectionTemplatePlaceholders_shouldReturnPlaceholderTemplate_IfDataProtectionAgencyContactDataIsNotSet() { // given + RestrictedTenantDTO tenantDTO = new RestrictedTenantDTO() + .content( + new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate())); when(tenantService.getRestrictedTenantDataByTenantId(anyLong())).thenReturn( - new RestrictedTenantDTO() - .content( - new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate()))); + tenantDTO); Agency agency = Agency.builder() .id(1000L) @@ -193,7 +257,7 @@ void renderDataProtectionTemplatePlaceholders_shouldReturnPlaceholderTemplate_If // when var renderedPlaceholders = centralDataProtectionTemplateService.renderDataProtectionPlaceholdersFromTemplates( - agency); + agency, tenantDTO); // then assertThat( @@ -208,10 +272,11 @@ void renderDataProtectionTemplatePlaceholders_shouldReturnPlaceholderTemplate_If void renderDataProtectionTemplatePlaceholders_shouldReturnPlaceholderTemplate_IfNoDataOnAgency() { // given + RestrictedTenantDTO tenantDTO = new RestrictedTenantDTO() + .content( + new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate())); when(tenantService.getRestrictedTenantDataByTenantId(anyLong())).thenReturn( - new RestrictedTenantDTO() - .content( - new Content().dataProtectionContactTemplate(getDataProtectionContactTemplate()))); + tenantDTO); Agency agency = Agency.builder() .id(1000L) @@ -223,7 +288,7 @@ void renderDataProtectionTemplatePlaceholders_shouldReturnPlaceholderTemplate_If // when var renderedPlaceholders = centralDataProtectionTemplateService.renderDataProtectionPlaceholdersFromTemplates( - agency); + agency, tenantDTO); // then assertThat(