Skip to content

Require privileges for unprotected CohortService methods (backport to 2.8.x)#6323

Merged
dkayiwa merged 1 commit into
openmrs:2.8.xfrom
dkayiwa:security/cohort-purge-authorization-2.8.x
Jul 15, 2026
Merged

Require privileges for unprotected CohortService methods (backport to 2.8.x)#6323
dkayiwa merged 1 commit into
openmrs:2.8.xfrom
dkayiwa:security/cohort-purge-authorization-2.8.x

Conversation

@dkayiwa

@dkayiwa dkayiwa commented Jul 15, 2026

Copy link
Copy Markdown
Member

Backport of #6317 to the 2.8.x release line.

CohortService.purgeCohort (permanent, irreversible cohort deletion) and CohortService.getCohorts(String) (cohort search) both lacked the @Authorized annotation that guards every other mutating and read method on the service, so they were reachable with no privilege check: purgeCohort via DELETE /ws/rest/v1/cohort/{uuid}?purge=true, and getCohorts via GET /ws/rest/v1/cohort?q=. This applies the Purge Cohorts and Get Patient Cohorts privileges respectively, matching the rest of the service, and adds regression tests asserting each gate. Addresses private advisory GHSA-4w59-mg6c-76ch.

Cherry-picked from d8fe136 on master; the only backport adjustment was reconciling the test file's import block with the 2.8.x import ordering.

* Require Purge Cohorts privilege for CohortService.purgeCohort

purgeCohort permanently and irreversibly removes a cohort from the
database but carried no @Authorized annotation, so any authenticated
user could reach it through the REST purge path
(DELETE /ws/rest/v1/cohort/{uuid}?purge=true), even though the
reversible voidCohort on the same resource requires Delete Cohorts.
A dedicated Purge Cohorts privilege already exists in PrivilegeConstants
but was never applied. Annotate purgeCohort with it, consistent with the
rest of the service.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Add regression test for Purge Cohorts authorization

Guards the @Authorized gate on CohortService.purgeCohort so that
dropping the annotation fails the build instead of silently
reopening the purge path to unprivileged callers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Require Get Patient Cohorts privilege for CohortService.getCohorts

getCohorts(String) was the only read on the service with no
@Authorized, reading straight from the DAO while every sibling
getter requires Get Patient Cohorts. A direct service-layer caller
could therefore list cohorts (names, descriptions, member patient
ids) with no authorization; the REST search path was only covered
incidentally by the resource's required-get privilege at conversion
time. Gate it at the service layer to match the other getters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Assert the specific privilege in cohort authorization tests

Capture the thrown APIAuthenticationException and assert its message
names the required privilege, so a future edit that swaps either
annotation to a different privilege (for purgeCohort, the exact
regression this PR fixes: gating on Get Patient Cohorts instead of
Purge Cohorts) fails the build rather than staying green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Use containsString matcher for cohort authorization assertions

assertThat(msg, containsString(privilege)) reports the actual message
on failure, so a future privilege regression is self-describing in the
CI log instead of the bare "expected true but was false" that
assertTrue(msg.contains(..)) produces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit d8fe136)
@sonarqubecloud

Copy link
Copy Markdown

@dkayiwa
dkayiwa merged commit 0712110 into openmrs:2.8.x Jul 15, 2026
7 checks passed
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.

1 participant