Skip to content

chore: Validate tracker events on tracker access manager [DHIS2-20158]#23508

Open
muilpp wants to merge 12 commits intomasterfrom
DHIS2-20158-events
Open

chore: Validate tracker events on tracker access manager [DHIS2-20158]#23508
muilpp wants to merge 12 commits intomasterfrom
DHIS2-20158-events

Conversation

@muilpp
Copy link
Copy Markdown
Contributor

@muilpp muilpp commented Apr 3, 2026

Centralizes all tracker event ACL logic into DefaultTrackerAccessManager, removing inline security checks from the SecurityTrackerEventValidator. That validator now delegates entirely to the access manager, simplifying the validation layer and consolidating access control logic in a single place.

Notable changes

  • TrackerAccessManager methods for TrackedEntity, Enrollment, and TrackerEvent now return List<ErrorMessage> instead of List<String>, carrying structured ValidationCodes and args.
  • Migrates all canRead methods from List<String> to List<ErrorMessage>, aligning them with the rest of the interface. That doesn't affect the exporter's response, because the ErrorMessage itself is never used there.
  • canUpdate for both TrackedEntity, Enrollment and TrackerEvent now accepts the payload org unit so capture scope can be checked only when the org unit actually changes. Previously the event validator inlined this check separately.
  • Adds a private helper method per access check (e.g. checkOrgUnitInCaptureScope, checkDataWriteAccessToProgram, checkOwnershipAccess) so each can* method reads as a flat list of named checks rather than inline.
  • Removed the null guard checks on org units for tracked entities and enrollments because the MandatoryFieldsValidator returns an error if the org unit is not set. Same goes for the test checkAccessPermissionForEnrollmentWhenOrgUnitIsNull. I think we test something that can't happen.

@muilpp muilpp marked this pull request as ready for review April 6, 2026 16:04
@muilpp muilpp requested a review from a team as a code owner April 6, 2026 16:04
@muilpp muilpp requested review from a team and ameenhere April 7, 2026 10:22
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes in the ValidationCode (some codes were removed, others were added) are the result of a bug fixing or they are a breaking change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm improving the readability, so I guess they would fall into the bug category.
This should not affect frontend nor Android, as they don't rely on the error messages I changed, they rely on the status code instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, I think we need to make them traceable for other clients.
It would be good if we mention these changes in the release-notes somehow. If any external app is relying on those codes, they should have an easy way to fix their app

List<ErrorMessage> errors = new ArrayList<>();
UserDetails user,
@Nonnull TrackedEntity trackedEntity,
@CheckForNull OrganisationUnit payloadTrackedEntityOrgUnit) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me this signature is confusing, I would either:

  1. Overload this method with a version without the orgUnit
  2. Make the orgUnit @nonnull. Then the client needs to always set an orgUnit, if there are no changes, it will use the orgUnit from the te itself

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to overload the method, as then it's easy to use the wrong one.
I'll make it not null and use the TE org unit, even in the relationship validator.

Comment on lines +162 to 170
List<String> canRead(UserDetails user, SingleEvent event);

List<String> canDelete(UserDetails user, TrackerEvent event);
List<String> canCreate(UserDetails user, SingleEvent event);

List<String> canRead(UserDetails user, Relationship relationship);

List<String> canCreate(UserDetails user, Relationship relationship);

List<String> canDelete(UserDetails user, @Nonnull Relationship relationship);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we changing single events and relationships in a later PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I'll do it in different PRs, if not it's a very long PR

@sonarqubecloud
Copy link
Copy Markdown

@muilpp muilpp requested a review from a team April 13, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants