diff --git a/api/agencyadminservice.yaml b/api/agencyadminservice.yaml index 84bfc8f0..a957c296 100644 --- a/api/agencyadminservice.yaml +++ b/api/agencyadminservice.yaml @@ -517,6 +517,10 @@ components: items: type: string enum: [ RELATIVE_COUNSELLING, SELF_COUNSELLING, PARENTAL_COUNSELLING ] + tenantId: + type: integer + format: int64 + example: 12 PostcodeRangeDTO: 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 598ebba9..da62c379 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 @@ -4,6 +4,7 @@ import static de.caritas.cob.agencyservice.api.exception.httpresponses.HttpStatusExceptionReason.AGENCY_IS_ALREADY_TEAM_AGENCY; import static de.caritas.cob.agencyservice.api.model.AgencyTypeRequestDTO.AgencyTypeEnum.TEAM_AGENCY; import static java.util.Objects.nonNull; +import static org.apache.commons.lang3.Validate.notNull; import com.google.common.base.Joiner; import de.caritas.cob.agencyservice.api.admin.service.agency.AgencyAdminFullResponseDTOBuilder; @@ -12,6 +13,7 @@ import de.caritas.cob.agencyservice.api.admin.validation.DeleteAgencyValidator; import de.caritas.cob.agencyservice.api.exception.httpresponses.ConflictException; import de.caritas.cob.agencyservice.api.exception.httpresponses.NotFoundException; +import de.caritas.cob.agencyservice.api.helper.AuthenticatedUser; import de.caritas.cob.agencyservice.api.model.AgencyAdminFullResponseDTO; import de.caritas.cob.agencyservice.api.model.AgencyDTO; import de.caritas.cob.agencyservice.api.model.AgencyTypeRequestDTO; @@ -29,6 +31,7 @@ import java.util.stream.Collectors; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -38,6 +41,7 @@ */ @Service @RequiredArgsConstructor +@Slf4j public class AgencyAdminService { private final @NonNull AgencyRepository agencyRepository; @@ -49,6 +53,11 @@ public class AgencyAdminService { private final @NonNull AgencyTopicMergeService agencyTopicMergeService; private final @NonNull AppointmentService appointmentService; + private final @NonNull AuthenticatedUser authenticatedUser; + @Value("${multitenancy.enabled}") + private boolean multitenancy; + + @Autowired(required = false) private AgencyTopicEnrichmentService agencyTopicEnrichmentService; @@ -99,7 +108,7 @@ public Agency findAgencyById(Long agencyId) { public AgencyAdminFullResponseDTO createAgency(AgencyDTO agencyDTO) { setDefaultCounsellingRelationsIfEmpty(agencyDTO); Agency agency = fromAgencyDTO(agencyDTO); - agency.setTenantId(TenantContext.getCurrentTenant()); + setTenantIdOnCreate(agencyDTO, agency); var savedAgency = agencyRepository.save(agency); enrichWithAgencyTopicsIfTopicFeatureEnabled(savedAgency); this.appointmentService.syncAgencyDataToAppointmentService(savedAgency); @@ -107,6 +116,26 @@ public AgencyAdminFullResponseDTO createAgency(AgencyDTO agencyDTO) { .fromAgency(); } + private void setTenantIdOnCreate(AgencyDTO agencyDTO, Agency agency) { + if (authenticatedUser.isTenantSuperAdmin()) { + notNull(agencyDTO.getTenantId()); + agency.setTenantId(agencyDTO.getTenantId()); + } else { + checkIfTenantIdMatch(agencyDTO); + agency.setTenantId(TenantContext.getCurrentTenant()); + } + } + + private void checkIfTenantIdMatch(AgencyDTO agencyDTO) { + if (agencyDTO.getTenantId() != null && !agencyDTO.getTenantId() + .equals(TenantContext.getCurrentTenant())) { + log.warn("TenantId of agencyDTO {} does not match current tenant {}", + agencyDTO.getTenantId(), TenantContext.getCurrentTenant()); + log.warn("Setting tenantId of agency {} to current tenant {}", + agencyDTO.getName(), TenantContext.getCurrentTenant()); + } + } + private void setDefaultCounsellingRelationsIfEmpty(AgencyDTO agencyDTO) { if (agencyDTO.getCounsellingRelations() == null || agencyDTO.getCounsellingRelations().isEmpty()) { agencyDTO.setCounsellingRelations(getAllPossibleCounsellingRelations()); @@ -141,6 +170,7 @@ private Agency fromAgencyDTO(AgencyDTO agencyDTO) { .createDate(LocalDateTime.now(ZoneOffset.UTC)) .updateDate(LocalDateTime.now(ZoneOffset.UTC)); + if (featureDemographicsEnabled && agencyDTO.getDemographics() != null) { demographicsConverter.convertToEntity(agencyDTO.getDemographics(), agencyBuilder); } diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/service/agency/AgencyAdminFullResponseDTOBuilder.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/service/agency/AgencyAdminFullResponseDTOBuilder.java index 6437e170..67b50d4b 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/admin/service/agency/AgencyAdminFullResponseDTOBuilder.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/service/agency/AgencyAdminFullResponseDTOBuilder.java @@ -11,7 +11,6 @@ import de.caritas.cob.agencyservice.api.repository.agency.Agency; import de.caritas.cob.agencyservice.api.repository.agencytopic.AgencyTopic; import java.util.List; -import java.util.stream.Collectors; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -48,6 +47,7 @@ private AgencyAdminResponseDTO createAgency() { .external((this.agency.isExternal())) .offline(this.agency.isOffline()) .topics(getTopics()) + .tenantId(this.agency.getTenantId()) .counsellingRelations(splitToList(agency.getCounsellingRelations())) .createDate(String.valueOf(this.agency.getCreateDate())) .updateDate(String.valueOf(this.agency.getUpdateDate())) @@ -62,7 +62,7 @@ private List splitToList(String return Lists.newArrayList(); } else { return Splitter.on(",").trimResults() - .splitToList(counsellingRelationsAsCommaSeparatedString).stream().map(AgencyAdminResponseDTO.CounsellingRelationsEnum::valueOf).collect(Collectors.toList()); + .splitToList(counsellingRelationsAsCommaSeparatedString).stream().map(AgencyAdminResponseDTO.CounsellingRelationsEnum::valueOf).toList(); } } @@ -83,7 +83,7 @@ private List getTopics() { } private List getTopics(List agencyTopics) { - return agencyTopics.stream().map(AgencyTopic::getTopicData).collect(Collectors.toList()); + return agencyTopics.stream().map(AgencyTopic::getTopicData).toList(); } private AgencyLinks createAgencyLinks() { diff --git a/src/main/java/de/caritas/cob/agencyservice/api/helper/AuthenticatedUser.java b/src/main/java/de/caritas/cob/agencyservice/api/helper/AuthenticatedUser.java index 9e1fb6fc..8bc26e74 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/helper/AuthenticatedUser.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/helper/AuthenticatedUser.java @@ -42,6 +42,11 @@ public boolean isAgencySuperAdmin() { return nonNull(roles) && roles.contains(Authority.AGENCY_ADMIN.getRoleName()); } + @JsonIgnore + public boolean isTenantSuperAdmin() { + return nonNull(roles) && roles.contains(Authority.TENANT_ADMIN.getRoleName()); + } + @JsonIgnore public boolean hasRestrictedAgencyPriviliges() { return isRestrictedAgencyAdmin() && !isAgencySuperAdmin(); diff --git a/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceITBase.java b/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceITBase.java index 215ed301..a76d3f86 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceITBase.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminServiceITBase.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.Lists; +import de.caritas.cob.agencyservice.api.helper.AuthenticatedUser; import de.caritas.cob.agencyservice.api.model.AgencyAdminFullResponseDTO; import de.caritas.cob.agencyservice.api.model.AgencyDTO; import de.caritas.cob.agencyservice.api.model.DemographicsDTO; @@ -18,11 +19,13 @@ import jakarta.persistence.EntityManager; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; public class AgencyAdminServiceITBase { @Autowired protected AgencyAdminService agencyAdminService; @Autowired protected AgencyRepository agencyRepository; + @MockBean protected AuthenticatedUser authenticatedUser; public void saveAgency_Should_PersistsAgency() { 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 fcd6f2ce..2351fbae 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 @@ -21,6 +21,7 @@ import de.caritas.cob.agencyservice.api.admin.validation.DeleteAgencyValidator; import de.caritas.cob.agencyservice.api.exception.httpresponses.ConflictException; import de.caritas.cob.agencyservice.api.exception.httpresponses.NotFoundException; +import de.caritas.cob.agencyservice.api.helper.AuthenticatedUser; import de.caritas.cob.agencyservice.api.model.AgencyAdminResponseDTO; import de.caritas.cob.agencyservice.api.model.AgencyTypeRequestDTO; import de.caritas.cob.agencyservice.api.model.DemographicsDTO; @@ -76,6 +77,9 @@ class AgencyAdminServiceTest { @Mock private Logger logger; + @Mock + AuthenticatedUser authenticatedUser; + @Captor private ArgumentCaptor agencyArgumentCaptor; private EasyRandom easyRandom; 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 aa785932..0ac536d9 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 @@ -33,6 +33,7 @@ import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -137,6 +138,51 @@ void createAgency_Should_returnStatusCreated_When_calledWithValidCreateParamsAnd .andExpect(jsonPath("_embedded.deleteDate").exists()); } + @Test + @WithMockUser(authorities = {"AUTHORIZATION_AGENCY_ADMIN", "AUTHORIZATION_TENANT_ADMIN"}) + void createAgency_Should_returnStatusCreated_And_SetTenantIdFromRequest_When_calledWithValidCreateParamsAndTenantSuperAdminAuthority() + throws Exception { + // given + when(authenticatedUser.isTenantSuperAdmin()).thenReturn(true); + when(consultingTypeManager.getConsultingTypeSettings(anyInt())) + .thenReturn(new ExtendedConsultingTypeResponseDTO()); + + AgencyDTO agencyDTO = new AgencyDTO() + .dioceseId(0L) + .name("Test name") + .description("Test description") + .postcode("12345") + .city("Test city") + .teamAgency(true) + .consultingType(0) + .url("https://www.test.de") + .external(true) + .tenantId(100L); + String payload = JsonConverter.convert(agencyDTO); + + // when, then + mockMvc.perform(post(PathConstants.CREATE_AGENCY_PATH) + .contentType(APPLICATION_JSON) + .content(payload)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("_embedded.id").exists()) + .andExpect(jsonPath("_embedded.name").value("Test name")) + .andExpect(jsonPath("_embedded.dioceseId").value(0)) + .andExpect(jsonPath("_embedded.city").value("Test city")) + .andExpect(jsonPath("_embedded.consultingType").value(0)) + .andExpect(jsonPath("_embedded.description").value("Test description")) + .andExpect(jsonPath("_embedded.postcode").value("12345")) + .andExpect(jsonPath("_embedded.teamAgency").value("true")) + .andExpect(jsonPath("_embedded.url").value("https://www.test.de")) + .andExpect(jsonPath("_embedded.external").value("true")) + .andExpect(jsonPath("_embedded.offline").exists()) + .andExpect(jsonPath("_embedded.tenantId").value("100")) + .andExpect(jsonPath("_embedded.topics").exists()) + .andExpect(jsonPath("_embedded.createDate").exists()) + .andExpect(jsonPath("_embedded.updateDate").exists()) + .andExpect(jsonPath("_embedded.deleteDate").exists()); + } + @Test @WithMockUser(authorities = "AUTHORIZATION_RESTRICTED_AGENCY_ADMIN") void createAgency_Should_returnAcessDenied_When_tryingToCreateAgencyAsRestrictedAgencyAdmin()