Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Update validation when no program specified [DHIS2-17236][2.39] #17357

Merged
merged 1 commit into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ public class TrackedEntityInstanceQueryParams {
/** Program for which instances in the response must be enrolled in. */
private Program program;

/** Programs to fetch. */
private List<Program> programs = List.of();

/** Status of the tracked entity instance in the given program. */
private ProgramStatus programStatus;

Expand Down Expand Up @@ -683,6 +686,15 @@ public TrackedEntityInstanceQueryParams setProgram(Program program) {
return this;
}

public List<Program> getPrograms() {
return programs;
}

public TrackedEntityInstanceQueryParams setPrograms(List<Program> programs) {
this.programs = programs;
return this;
}

public ProgramStage getProgramStage() {
return programStage;
}
Expand Down Expand Up @@ -935,11 +947,6 @@ public User getUser() {
return user;
}

public TrackedEntityInstanceQueryParams setUser(User user) {
this.user = user;
return this;
}

public List<OrderParam> getOrders() {
return orders;
}
Expand Down Expand Up @@ -977,6 +984,11 @@ public TrackedEntityInstanceQueryParams setAssignedUsers(Set<String> assignedUse
return this;
}

public TrackedEntityInstanceQueryParams setUser(User user) {
this.user = user;
return this;
}

public List<TrackedEntityType> getTrackedEntityTypes() {
return trackedEntityTypes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@
import org.hisp.dhis.dxf2.events.event.EventContext;
import org.hisp.dhis.organisationunit.OrganisationUnit;
import org.hisp.dhis.organisationunit.OrganisationUnitService;
import org.hisp.dhis.program.Program;
import org.hisp.dhis.program.ProgramService;
import org.hisp.dhis.security.Authorities;
import org.hisp.dhis.security.acl.AclService;
import org.hisp.dhis.system.grid.ListGrid;
Expand Down Expand Up @@ -110,6 +112,8 @@ public class DefaultTrackedEntityInstanceService implements TrackedEntityInstanc

private final AclService aclService;

private final ProgramService programService;

private final TrackerOwnershipManager trackerOwnershipAccessManager;

private final TrackedEntityInstanceAuditService trackedEntityInstanceAuditService;
Expand All @@ -128,6 +132,7 @@ public DefaultTrackedEntityInstanceService(
OrganisationUnitService organisationUnitService,
CurrentUserService currentUserService,
AclService aclService,
ProgramService programService,
@Lazy TrackerOwnershipManager trackerOwnershipAccessManager,
@Lazy TrackedEntityInstanceAuditService trackedEntityInstanceAuditService,
@Lazy TrackedEntityAttributeValueAuditService attributeValueAuditService) {
Expand All @@ -149,6 +154,7 @@ public DefaultTrackedEntityInstanceService(
this.organisationUnitService = organisationUnitService;
this.currentUserService = currentUserService;
this.aclService = aclService;
this.programService = programService;
this.trackerOwnershipAccessManager = trackerOwnershipAccessManager;
this.trackedEntityInstanceAuditService = trackedEntityInstanceAuditService;
this.attributeValueAuditService = attributeValueAuditService;
Expand All @@ -171,6 +177,10 @@ public List<TrackedEntityInstance> getTrackedEntityInstances(
params.addFiltersIfNotExist(QueryItem.getQueryItems(attributes));
}

if (params.getProgram() == null) {
params.setPrograms(getTrackerPrograms(params.getUser()));
}

decideAccess(params);
// AccessValidation should be skipped only and only if it is internal
// service that runs the task (for example sync job)
Expand Down Expand Up @@ -221,6 +231,10 @@ public List<Long> getTrackedEntityInstanceIds(

handleSortAttributes(params);

if (params.getProgram() == null) {
params.setPrograms(getTrackerPrograms(params.getUser()));
}

decideAccess(params);

// AccessValidation should be skipped only and only if it is internal
Expand Down Expand Up @@ -509,7 +523,7 @@ public void validate(TrackedEntityInstanceQueryParams params) throws IllegalQuer
throw new IllegalQueryException("Params cannot be null");
}

User user = params.getUser();
User user = currentUserService.getCurrentUser();

if (!params.hasTrackedEntityInstances()
&& !params.hasOrganisationUnits()
Expand Down Expand Up @@ -920,4 +934,11 @@ private void addTrackedEntityInstanceAudit(
trackedEntityInstanceAuditService.addTrackedEntityInstanceAudit(trackedEntityInstanceAudit);
}
}

private List<Program> getTrackerPrograms(User user) {
return programService.getAllPrograms().stream()
.filter(Program::isRegistration)
.filter(p -> aclService.canDataRead(user, p))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
import org.hisp.dhis.common.QueryItem;
import org.hisp.dhis.common.QueryOperator;
import org.hisp.dhis.common.hibernate.SoftDeleteHibernateObjectStore;
import org.hisp.dhis.commons.collection.CollectionUtils;
import org.hisp.dhis.commons.util.SqlHelper;
import org.hisp.dhis.dxf2.events.event.EventContext;
import org.hisp.dhis.event.EventStatus;
Expand Down Expand Up @@ -482,6 +481,7 @@ private String getFromSubQuery(
.append(" FROM trackedentityinstance TEI ")

// INNER JOIN on constraints
.append(joinPrograms(params))
.append(getFromSubQueryJoinAttributeConditions(params))
.append(getFromSubQueryJoinProgramOwnerConditions(params))
.append(getFromSubQueryJoinOrgUnitConditions(params))
Expand Down Expand Up @@ -545,7 +545,23 @@ private String getFromSubQuerySelect(TrackedEntityInstanceQueryParams params) {
}
}

return "SELECT " + String.join(", ", columns);
return "SELECT DISTINCT " + String.join(", ", columns);
}

private String joinPrograms(TrackedEntityInstanceQueryParams params) {
StringBuilder trackedEntity = new StringBuilder();

trackedEntity.append(" INNER JOIN program P ");
trackedEntity.append(" ON P.trackedentitytypeid = TEI.trackedentitytypeid ");

if (!params.hasProgram()) {
trackedEntity
.append("AND P.programid IN (")
.append(getCommaDelimitedString(getIdentifiers(params.getPrograms())))
.append(")");
}

return trackedEntity.toString();
}

/**
Expand Down Expand Up @@ -573,26 +589,25 @@ private String getFromSubQueryTrackedEntityConditions(
SqlHelper whereAnd, TrackedEntityInstanceQueryParams params) {
StringBuilder trackedEntity = new StringBuilder();

if (params.hasTrackedEntityType()) {
trackedEntity
.append(whereAnd.whereAnd())
.append("TEI.trackedentitytypeid = ")
.append(params.getTrackedEntityType().getId())
.append(SPACE);
} else if (!CollectionUtils.isEmpty(params.getTrackedEntityTypes())) {
if (params.hasTrackedEntityInstances()) {
trackedEntity
.append(whereAnd.whereAnd())
.append("TEI.trackedentitytypeid IN (")
.append(getCommaDelimitedString(getIdentifiers(params.getTrackedEntityTypes())))
.append("TEI.uid IN (")
.append(encodeAndQuote(params.getTrackedEntityInstanceUids()))
.append(") ");
}

if (params.hasTrackedEntityInstances()) {
if (params.hasTrackedEntityType()) {
trackedEntity
.append(whereAnd.whereAnd())
.append("TEI.uid IN (")
.append(encodeAndQuote(params.getTrackedEntityInstanceUids()))
.append(") ");
.append("TEI.trackedentitytypeid = ")
.append(params.getTrackedEntityType().getId());
} else if (!params.hasProgram()) {
trackedEntity
.append(whereAnd.whereAnd())
.append("TEI.trackedentitytypeid in (")
.append(getCommaDelimitedString(getIdentifiers(params.getTrackedEntityTypes())))
.append(")");
}

if (params.hasLastUpdatedDuration()) {
Expand Down Expand Up @@ -796,16 +811,22 @@ private String getFromSubQueryJoinOrderByAttributes(TrackedEntityInstanceQueryPa
*/
private String getFromSubQueryJoinProgramOwnerConditions(
TrackedEntityInstanceQueryParams params) {
if (!params.hasProgram() || skipOwnershipCheck(params)) {

if (skipOwnershipCheck(params)) {
return "";
}

return new StringBuilder()
.append(" INNER JOIN trackedentityprogramowner PO ")
.append("ON PO.programid = ")
.append(params.getProgram().getId())
.append(" AND PO.trackedentityinstanceid = TEI.trackedentityinstanceid ")
.toString();
if (params.hasProgram()) {
return " INNER JOIN trackedentityprogramowner PO "
+ " ON PO.programid = "
+ params.getProgram().getId()
+ " AND PO.trackedentityinstanceid = TEI.trackedentityinstanceid "
+ " AND P.programid = PO.programid";
}

return "LEFT JOIN trackedentityprogramowner PO ON "
+ " PO.trackedentityinstanceid = TEI.trackedentityinstanceid"
+ " AND P.programid = PO.programid";
}

/**
Expand All @@ -825,10 +846,7 @@ private String getFromSubQueryJoinOrgUnitConditions(TrackedEntityInstanceQueryPa
orgUnits
.append(" INNER JOIN organisationunit OU ")
.append("ON OU.organisationunitid = ")
.append(
params.hasProgram() && !skipOwnershipCheck(params)
? "PO.organisationunitid "
: "TEI.organisationunitid ");
.append(getOwnerOrgUnit(params));

if (!params.hasOrganisationUnits()) {
return orgUnits.toString();
Expand Down Expand Up @@ -857,6 +875,19 @@ private String getFromSubQueryJoinOrgUnitConditions(TrackedEntityInstanceQueryPa
return orgUnits.toString();
}

private String getOwnerOrgUnit(TrackedEntityInstanceQueryParams params) {

if (skipOwnershipCheck(params)) {
return "TEI.organisationunitid ";
}

if (params.hasProgram()) {
return "PO.organisationunitid ";
}

return "COALESCE(PO.organisationunitid, TEI.organisationunitid) ";
}

/**
* Generates an INNER JOIN for program instances. If the param we need to order by is enrolledAt,
* we need to join the program instance table to be able to select and order by this value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.hisp.dhis.organisationunit.OrganisationUnit;
import org.hisp.dhis.organisationunit.OrganisationUnitService;
import org.hisp.dhis.program.Program;
import org.hisp.dhis.program.ProgramService;
import org.hisp.dhis.security.acl.AclService;
import org.hisp.dhis.trackedentityattributevalue.TrackedEntityAttributeValueAuditService;
import org.hisp.dhis.trackedentityattributevalue.TrackedEntityAttributeValueService;
Expand Down Expand Up @@ -66,6 +67,8 @@ class DefaultTrackedEntityInstanceServiceTest {

@Mock private AclService aclService;

@Mock private ProgramService programService;

@Mock private TrackerOwnershipManager trackerOwnershipAccessManager;

@Mock private TrackedEntityInstanceAuditService trackedEntityInstanceAuditService;
Expand All @@ -87,6 +90,7 @@ void setup() {
organisationUnitService,
currentUserService,
aclService,
programService,
trackerOwnershipAccessManager,
trackedEntityInstanceAuditService,
attributeValueAuditService);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ protected void setUpTest() throws Exception {
programA.setCategoryCombo(categoryComboA);
programA.setUid(CodeGenerator.generateUid());
programA.setCode(RandomStringUtils.randomAlphanumeric(10));
programA.setTrackedEntityType(trackedEntityTypeA);
CategoryOptionCombo defaultCategoryOptionCombo = createCategoryOptionCombo('A');
defaultCategoryOptionCombo.setCategoryCombo(categoryComboA);
defaultCategoryOptionCombo.setUid(DEF_COC_UID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ void testEnrollmentFollowup() {
queryParams.setOrganisationUnits(Sets.newHashSet(organisationUnitA));
queryParams.setTrackedEntityType(trackedEntityTypeA);
queryParams.setIncludeAllAttributes(true);
queryParams.setPrograms(List.of(programA));
TrackedEntityInstanceParams params =
new TrackedEntityInstanceParams(
false, TrackedEntityInstanceEnrollmentParams.TRUE, false, false, false, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.hisp.dhis.dxf2.events.TrackedEntityInstanceParams;
import org.hisp.dhis.dxf2.events.trackedentity.TrackedEntityInstance;
import org.hisp.dhis.dxf2.events.trackedentity.TrackedEntityInstanceService;
import org.hisp.dhis.program.ProgramService;
import org.hisp.dhis.trackedentity.TrackedEntityInstanceQueryParams;
import org.hisp.dhis.trackedentity.TrackedEntityType;
import org.hisp.dhis.user.User;
Expand All @@ -50,6 +51,7 @@
*/
class TrackedEntityInstanceAttributesAggregateAclTest extends TrackerTest {
@Autowired private TrackedEntityInstanceService trackedEntityInstanceService;
@Autowired private ProgramService programService;

private User superUser;

Expand Down Expand Up @@ -95,6 +97,8 @@ void verifyTeiCanBeAccessedWhenDATA_READPublicAccessOnTrackedEntityType() {
ImmutableMap.of("trackedEntityType", trackedEntityType));
this.persistTrackedEntityInstance();
this.persistTrackedEntityInstance();
programA.setTrackedEntityType(trackedEntityType);
programService.addProgram(programA);
});
final TrackedEntityType trackedEntityType =
trackedEntityTypeService.getTrackedEntityType(tetUid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ private void populatePrerequisites(boolean removeOwnership) {
programB.setAccessLevel(AccessLevel.PROTECTED);
programB.setUid(CodeGenerator.generateUid());
programB.setCode(RandomStringUtils.randomAlphanumeric(10));
programB.setTrackedEntityType(trackedEntityTypeA);
Set<UserAccess> programBUserAccess = new HashSet<>();
programBUserAccess.add(new UserAccess(currentUser, AccessStringHelper.FULL));
programB.setUserAccesses(programBUserAccess);
Expand All @@ -252,6 +253,7 @@ private void populatePrerequisites(boolean removeOwnership) {
programA.setCategoryCombo(categoryComboA);
programA.setUid(CodeGenerator.generateUid());
programA.setCode(RandomStringUtils.randomAlphanumeric(10));
programA.setTrackedEntityType(trackedEntityTypeA);
programA.setProgramStages(
Stream.of(programStageA1, programStageA2)
.collect(Collectors.toCollection(HashSet::new)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/
package org.hisp.dhis.dxf2.sync;

import static org.hisp.dhis.security.acl.AccessStringHelper.FULL;
import static org.hisp.dhis.utils.Assertions.assertContainsOnly;
import static org.hisp.dhis.utils.Assertions.assertIsEmpty;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -40,6 +41,7 @@
import org.hisp.dhis.dxf2.events.TrackedEntityInstanceParams;
import org.hisp.dhis.dxf2.events.trackedentity.TrackedEntityInstanceService;
import org.hisp.dhis.organisationunit.OrganisationUnit;
import org.hisp.dhis.program.Program;
import org.hisp.dhis.test.integration.SingleSetupIntegrationTestBase;
import org.hisp.dhis.trackedentity.TrackedEntityAttribute;
import org.hisp.dhis.trackedentity.TrackedEntityInstance;
Expand All @@ -50,6 +52,7 @@
import org.hisp.dhis.trackedentityattributevalue.TrackedEntityAttributeValueService;
import org.hisp.dhis.user.User;
import org.hisp.dhis.user.UserService;
import org.hisp.dhis.user.sharing.Sharing;
import org.hisp.dhis.util.DateUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -90,6 +93,10 @@ private void prepareDataForTest() {
tet.getTrackedEntityTypeAttributes().add(tetaA);
tet.getTrackedEntityTypeAttributes().add(tetaB);
manager.save(tet);
Program program = createProgram('a');
program.setTrackedEntityType(tet);
program.setSharing(Sharing.builder().publicAccess(FULL).build());
manager.save(program);
OrganisationUnit ou = createOrganisationUnit('a');
manager.save(ou);
TrackedEntityInstance teiToSync = createTrackedEntityInstance('a', ou, teaA);
Expand Down
Loading
Loading