From 1d49336308a213ac3a84a0df130d1103f11f43bd Mon Sep 17 00:00:00 2001 From: marc Date: Tue, 10 Dec 2024 16:17:12 +0100 Subject: [PATCH] fix: Validate data sharing on program stage [DHIS2-17594] (#19423) * fix: Validate data sharing on program stage [DHIS2-17594] * fix: Validate data sharing on program stage [DHIS2-17594] --- .../DefaultTrackedEntityService.java | 5 +- .../TrackedEntityServiceTest.java | 88 ++++++++++++++++++- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java index a17f1e891b33..8f04cc214766 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/export/trackedentity/DefaultTrackedEntityService.java @@ -397,7 +397,10 @@ private Set getEnrollments( e -> { Set filteredEvents = e.getEvents().stream() - .filter(event -> includeDeleted || !event.isDeleted()) + .filter( + event -> + (includeDeleted || !event.isDeleted()) + && trackerAccessManager.canRead(user, event, false).isEmpty()) .collect(Collectors.toSet()); e.setEvents(filteredEvents); return e; diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java index 671da213c93d..d797b3ccf943 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/export/trackedentity/TrackedEntityServiceTest.java @@ -161,6 +161,8 @@ class TrackedEntityServiceTest extends PostgresIntegrationTestBase { private Enrollment enrollmentB; + private ProgramStage programStageA1; + private Event eventA; private Event eventB; @@ -173,8 +175,6 @@ class TrackedEntityServiceTest extends PostgresIntegrationTestBase { private TrackedEntity trackedEntityGrandchildA; - private Note note; - private CategoryOptionCombo defaultCategoryOptionCombo; private Relationship relationshipA; @@ -270,7 +270,7 @@ void setUp() { programA.setCategoryCombo(defaultCategoryCombo); programA.setMinAttributesRequiredToSearch(0); manager.save(programA, false); - ProgramStage programStageA1 = createProgramStage(programA); + programStageA1 = createProgramStage(programA); programStageA1.setPublicAccess(AccessStringHelper.FULL); manager.save(programStageA1, false); ProgramStage programStageA2 = createProgramStage(programA); @@ -360,7 +360,7 @@ void setUp() { eventA.setCompletedDate(parseDate("2021-02-27T11:05:00.000")); eventA.setCompletedBy("herb"); eventA.setAssignedUser(user); - note = new Note("note1", "ant"); + Note note = new Note("note1", "ant"); note.setUid(generateUid()); note.setCreated(new Date()); note.setLastUpdated(new Date()); @@ -1342,6 +1342,37 @@ void shouldReturnTrackedEntityWithoutEventsGivenTheyShouldNotBeIncluded() assertIsEmpty(enrollmentA.get().getEvents()); } + @Test + void shouldReturnTrackedEntityWithoutEventWhenProgramStageNotAccessible() + throws ForbiddenException, BadRequestException, NotFoundException { + injectAdminIntoSecurityContext(); + programStageA1.setSharing(Sharing.builder().publicAccess("--------").build()); + manager.update(programStageA1); + injectSecurityContextUser(user); + TrackedEntityParams params = new TrackedEntityParams(false, TRUE, true, false); + TrackedEntityOperationParams operationParams = + TrackedEntityOperationParams.builder() + .organisationUnits(orgUnitA) + .orgUnitMode(SELECTED) + .trackedEntityType(trackedEntityTypeA) + .trackedEntities(trackedEntityA) + .trackedEntityParams(params) + .build(); + + List trackedEntities = trackedEntityService.getTrackedEntities(operationParams); + + assertContainsOnly(List.of(trackedEntityA), trackedEntities); + assertContainsOnly( + Set.of(enrollmentA.getUid(), enrollmentB.getUid()), + uids(trackedEntities.get(0).getEnrollments())); + List enrollments = new ArrayList<>(trackedEntities.get(0).getEnrollments()); + Optional enrollmentA = + enrollments.stream() + .filter(enrollment -> enrollment.getUid().equals(this.enrollmentA.getUid())) + .findFirst(); + assertIsEmpty(enrollmentA.get().getEvents()); + } + @Test void shouldReturnTrackedEntityMappedCorrectly() throws ForbiddenException, NotFoundException, BadRequestException { @@ -2023,6 +2054,55 @@ void shouldReturnTrackedEntityTypeAttributesWhenSingleTERequestedAndNoProgramSpe UID.of(trackedEntityA), UID.of(programA), TrackedEntityParams.TRUE)); } + @Test + void shouldFindTrackedEntityWithEventsWhenEventRequestedAndAccessible() + throws ForbiddenException, NotFoundException, BadRequestException { + injectAdminIntoSecurityContext(); + User testUser = createAndAddUser(false, "testUser", emptySet(), emptySet(), "F_EXPORT_DATA"); + testUser.setOrganisationUnits(Set.of(orgUnitA)); + manager.update(testUser); + injectSecurityContext(UserDetails.fromUser(testUser)); + + TrackedEntity trackedEntity = + trackedEntityService.getTrackedEntity( + UID.of(trackedEntityA), UID.of(programA), TrackedEntityParams.TRUE); + + assertEquals(trackedEntityA.getUid(), trackedEntity.getUid()); + assertContainsOnly(Set.of(enrollmentA.getUid()), uids(trackedEntity.getEnrollments())); + List enrollments = new ArrayList<>(trackedEntity.getEnrollments()); + Optional enrollmentA = + enrollments.stream() + .filter(enrollment -> enrollment.getUid().equals(this.enrollmentA.getUid())) + .findFirst(); + Set events = enrollmentA.get().getEvents(); + assertContainsOnly(Set.of(eventA.getUid()), uids(events)); + } + + @Test + void shouldFindTrackedEntityWithoutEventsWhenEventRequestedButNotAccessible() + throws ForbiddenException, NotFoundException, BadRequestException { + injectAdminIntoSecurityContext(); + programStageA1.setSharing(Sharing.builder().publicAccess("--------").build()); + manager.update(programStageA1); + User testUser = createAndAddUser(false, "testUser", emptySet(), emptySet(), "F_EXPORT_DATA"); + testUser.setOrganisationUnits(Set.of(orgUnitA)); + manager.update(testUser); + injectSecurityContext(UserDetails.fromUser(testUser)); + + TrackedEntity trackedEntity = + trackedEntityService.getTrackedEntity( + UID.of(trackedEntityA), UID.of(programA), TrackedEntityParams.TRUE); + + assertEquals(trackedEntityA.getUid(), trackedEntity.getUid()); + assertContainsOnly(Set.of(enrollmentA.getUid()), uids(trackedEntity.getEnrollments())); + List enrollments = new ArrayList<>(trackedEntity.getEnrollments()); + Optional enrollmentA = + enrollments.stream() + .filter(enrollment -> enrollment.getUid().equals(this.enrollmentA.getUid())) + .findFirst(); + assertIsEmpty(enrollmentA.get().getEvents()); + } + private Set attributeNames(final Collection attributes) { // depends on createTrackedEntityAttribute() prefixing with "Attribute" return attributes.stream()