Sonador ACL MS1: Test Coverage Tracking
This issue is the canonical tracking location for all functional test work related to the Sonador ACL security model. Implementation status, coverage gaps, and priorities are maintained here. Child issues cover specific areas in depth.
Child issues:
- oak-tree/medical-imaging/sonador#64 — Test matrix and full coverage assessment (closed July 2026 — all deliverables traced to implementing MRs in the close-out verification)
-
#73 (closed) —
aclpermission functional tests (complete July 2026 — 11/11 implemented and passing live; see the ACL Policy Management section below)
Implementing merge requests (ACL close-out, June–July 2026):
June 2026 wave:
- oak-tree/medical-imaging/sonador!88 — Sonador-side ACL enforcement fixes (worklist regression, comment permissions, modify/remove ancestor traversal)
- orthanc-sonador!53 (merged) — Orthanc cloud-plugin policy-builder fixes (companion to oak-tree/medical-imaging/sonador!88)
- sonador-ftests!8 (merged) — the functional test matrix inventoried below
- oak-tree/medical-imaging/sonador-client!71 — IO-client test hardening the suite depends on
July 2026 wave (acl permission + worklist authorization):
-
oak-tree/medical-imaging/sonador!91 —
aclpermission enforcement:resource_perm()acl branch, DICOMweb ACL-management route backfill, descend-only scope (merged 2026-07-09) -
orthanc-sonador!56 (merged) —
acl_traverseancestor-traversal signal in the local policy builder; companion to oak-tree/medical-imaging/sonador!91 (merged 2026-07-09) -
orthanc-authorization!2 (merged) —
aclaction token in the route parser; companion to oak-tree/medical-imaging/sonador!91 (all three deploy together) -
sonador-ftests!13 (merged) —
aclpermission functional tests, 11 cases intests/tests_sonadoracl_manage.py(merged 2026-07-09) -
oak-tree/medical-imaging/sonador-client!74 — shared
assertRejected/assertDeniedhelpers (dependency of sonador-ftests!13 (merged)) -
oak-tree/medical-imaging/sonador!90 / orthanc-sonador!55 (merged) — worklist authorization no longer requires local
Modify; test updates in sonador-ftests!12 (merged) and oak-tree/medical-imaging/sonador-client!73 (merged 2026-07-08)
Documentation
The documentation/diagram checklist that previously lived here has been moved to its own tracking issue: #76 ("Systems & Workflow Documentation — SaMD diagram coverage"). That issue carries the full SaMD documentation assessment and current-coverage analysis. The original checklist is preserved as a comment on this issue for traceability. This issue now scopes functional-test coverage only.
Test Suite: Implemented
Tests live in oak-tree/medical-imaging/sonador-ftests. The tests/ directory holds tests added as part of this milestone; sonador/ftests/ holds the pre-existing suite that ships with the IO client library.
Server-Level Permissions
| Test | Permission | Module |
|---|---|---|
testacl_server_upload_data |
upload: allow → deny cycle |
tests/tests_sonadoracl_global.py |
testacl_server_query_data |
query: allow → deny cycle |
tests/tests_sonadoracl_global.py |
Global Resource Permissions (server-scoped policy)
| Test | Resource | Permission | Module |
|---|---|---|---|
testacl_sonador_series_access_permission |
Series | view | tests/tests_sonadoracl_global.py |
testacl_sonador_study_access_permission |
Study | view | tests/tests_sonadoracl_global.py |
testacl_sonador_patient_access_permission |
Patient | view | tests/tests_sonadoracl_global.py |
testacl_global_series_modify_permission |
Series | modify | tests/tests_sonadoracl_modify_remove.py |
testacl_global_study_modify_permission |
Study | modify | tests/tests_sonadoracl_modify_remove.py |
testacl_global_patient_modify_permission |
Patient | modify | tests/tests_sonadoracl_modify_remove.py |
testacl_global_series_remove_permission |
Series | remove | tests/tests_sonadoracl_modify_remove.py |
testacl_global_study_remove_permission |
Study | remove | tests/tests_sonadoracl_modify_remove.py |
testacl_global_patient_remove_permission |
Patient | remove | tests/tests_sonadoracl_modify_remove.py |
testacl_series_query_allow_deny |
Series | query | tests/tests_sonadoracl_query.py |
testacl_study_query_allow_deny |
Study | query (child series) | tests/tests_sonadoracl_query.py |
testacl_patient_query_allow_deny |
Patient | query (all children) | tests/tests_sonadoracl_query.py |
Local Resource Permissions (Orthanc resource-level ACL)
| Test | Resource | ACL Scope | Permission | Module |
|---|---|---|---|---|
testacl_local_series_view_data_tenancy |
Series | user | view: limited user uploads (upload-only); upload alone ≠ view; grant series user ACL → allow → revoke → deny | tests/tests_sonadoracl_local_view.py |
testacl_local_patient_view_user_grant |
Patient | user | view: patient grant propagates to child series → revoke → deny | tests/tests_sonadoracl_local_view.py |
testacl_local_patient_view_group_grant |
Patient | group | view: patient group grant propagates to child series → revoke → deny | tests/tests_sonadoracl_local_view.py |
testacl_local_series_modify_user_acl |
Series | user | modify | tests/tests_sonadoracl_modify_remove.py |
testacl_local_series_modify_group_acl |
Series | group | modify | tests/tests_sonadoracl_modify_remove.py |
testacl_local_series_remove_user_acl |
Series | user | remove | tests/tests_sonadoracl_modify_remove.py |
testacl_local_series_remove_group_acl |
Series | group | remove | tests/tests_sonadoracl_modify_remove.py |
testacl_local_study_comment_view_deny_edit |
Study | group | comment_view ✓, comment_edit ✗ | tests/tests_sonadoracl_local_comments.py |
testacl_local_study_comment_edit_authorized |
Study | group | comment_edit override via local grant | tests/tests_sonadoracl_local_comments.py |
testacl_local_study_comment_view_denied |
Study | group | comment_view blocked by local policy | tests/tests_sonadoracl_local_comments.py |
test_user_acl_management |
Series → Study | user | view + DICOMweb cross-check | sonador/ftests/tests_sonadorauth_acl.py |
test_group_acl_management |
Series → Study | group | view + DICOMweb cross-check | sonador/ftests/tests_sonadorauth_acl.py |
ACL Policy Management (acl permission) — added July 2026
Full specification in #73 (closed); implemented in tests/tests_sonadoracl_manage.py (SonadorAclPermissionTests) via sonador-ftests!13 (merged), passing 11/11 live against dev01. Enforcement landed across oak-tree/medical-imaging/sonador!91, orthanc-sonador!56 (merged), and orthanc-authorization!2 (merged) (deploy together). Pinned design decision: acl does not imply view; scope semantics are descend-only.
| Test | Scope | Coverage | Module |
|---|---|---|---|
testacl_global_wildcard |
Global *
|
Creator can create local policies at patient/study/series; policies authorize the beneficiary | tests/tests_sonadoracl_manage.py |
testacl_global_scoped_patient |
Global patient={uid}
|
Grant reaches the patient and all descendant studies/series | tests/tests_sonadoracl_manage.py |
testacl_global_scoped_study |
Global study={uid}
|
Grant reaches the study + child series; denied on a sibling study | tests/tests_sonadoracl_manage.py |
testacl_global_scoped_series |
Global series={uid}
|
Grant reaches only the series; denied on parent study and sibling series | tests/tests_sonadoracl_manage.py |
testacl_local_scoped_patient |
Local patient ACL |
ACL=True grant cascades to child study/series policy creation |
tests/tests_sonadoracl_manage.py |
testacl_local_scoped_study |
Local study ACL |
ACL=True grant cascades to child series policy creation |
tests/tests_sonadoracl_manage.py |
testacl_local_scoped_series |
Local series ACL | Grant reaches only the series; denied on parent study | tests/tests_sonadoracl_manage.py |
testacl_negative_query_not_acl_dicomweb |
Negative |
query=True, acl=False → 403 via the DICOMweb ACL-management route |
tests/tests_sonadoracl_manage.py |
testacl_negative_modify_not_acl_internal |
Negative |
modify=True, acl=False → 403 via the internal route (the pre-fix vulnerability, confirmed live) |
tests/tests_sonadoracl_manage.py |
testacl_negative_revocation |
Negative | Global and local revocation variants → 403 on further policy creation | tests/tests_sonadoracl_manage.py |
testacl_negative_acl_without_view_separation |
Negative |
acl=True, view=False can create/enumerate policies but is denied view-mediated retrieval |
tests/tests_sonadoracl_manage.py |
Role Matrix
Canonical role definitions live in cli/roles.py (ADMIN, REVIEWER, TECHNICIAN, REFERRING). One test per role × resource-type combination is in tests/tests_sonadoracl_roles.py (12 methods: 4 roles × 3 resource types). See oak-tree/medical-imaging/sonador#64 for the full matrix.
| Role | Series | Study | Patient | Permissions asserted per test |
|---|---|---|---|---|
| Admin | view, comment_view, comment_edit, modify (all granted) | |||
| Reviewer | view ✓, comment_view ✓, comment_edit ✓, modify ✗ | |||
| Technician | view ✓, comment_view ✗, comment_edit ✗, modify ✗ | |||
| Referring | view ✓, comment_view ✓, comment_edit ✗, modify ✗ |
Worklist / MS2 Integration
| Test | Description | Module |
|---|---|---|
testacl_worklist_reassignment_access_denied |
User reassigned off a study loses access; new assignee gains access (View-only grants — local Modify no longer required per oak-tree/medical-imaging/sonador!90 / orthanc-sonador!55 (merged); test updated in sonador-ftests!12 (merged)) |
tests/tests_sonadoracl_worklist_security.py |
test_history_acl_parity |
Worklist item + history readable with local View; state/procedure writes succeed without local Modify (updated in sonador-ftests!12 (merged)) |
tests/tests_ext_worklist_procedures.py |
Test Suite: Remaining Gaps
Priority 1 — Blocking
-
aclpermission:no test anywhere verifies...Closed out July 2026. All 11 test cases specified in #73 (closed) (4 global-scoped + 3 local-scoped + 4 negative/regression) implemented intests/tests_sonadoracl_manage.py(sonador-ftests!13 (merged)) and passing live against dev01. Required a precursor enforcement fix —resource_perm()had no branch returningself.acl, so ACL-management requests fell through toview/modify/remove(a real, confirmed vulnerability: amodify=True, acl=Falseuser could create ACL policies) — landed across oak-tree/medical-imaging/sonador!91, orthanc-sonador!56 (merged), and orthanc-authorization!2 (merged) (all three deploy together). See #73 (closed) for full detail. Full test-by-test trace is in the "ACL Policy Management" section above.
Priority 2 — Important
-
uploaddeny boundary:testacl_server_upload_dataverifies the allow cycle and revocation flag, but no test creates a group withupload=Falseand asserts thatupload_image()raises 403 for that group specifically. -
Local
modifyandremoveat study and patient scope: current local tests cover series scope only. Tests at study and patient scope use server-level scoped policies rather than Orthanc resource-level ACL (study.create_group_acl({...})/patient.create_group_acl({...})). -
DICOMweb cross-checks in new tests: only the pre-existing
test_user_acl_managementandtest_group_acl_managementverify that DICOMweb endpoints (WADO-RS/QIDO-RS) honor ACL decisions. The 32 tests added in June 2026 do not include DICOMweb assertions. (Partial progress July 2026:testacl_negative_query_not_acl_dicomwebexercises a DICOMweb deny for the acl route; a positive-grant DICOMweb acl case is still outstanding — noted as a known gap in sonador-ftests!13 (merged).)
Priority 3 — API Surface
-
query_study()andquery_patient()direct calls: the threetestacl_*_query_allow_denytests callquery_series()to verify inheritance from study/patient-level policies. Thequery_study()andquery_patient()API methods are never called directly in any ACL test. -
anonymize()operation: zero coverage across all test files. This is a destructive write operation with a separate Orthanc authorization path that must be confirmed to requiremodifypermission. -
filearchive()endpoint: ACL enforcement on the Orthanc archive download URL is untested.tests_download.pytests DICOMweb-based downloads, not thefilearchivepath.
Priority 4 — Future
-
fetch_attachments(),attachment_info(),attachment_data()— no coverage. -
reconstruct(),fetch_meta()— no coverage. -
Specialized DICOM resource types:
query_sr(),query_seg(),query_m3d(),query_doc()— no ACL coverage.
Notes
- Feb 15, 2024: Sonador was notified of scope changes due to updates to the Orthanc Advanced Authorization Plugin. Validation of resource access will be managed via the Orthanc–Sonador integration rather than a custom filter function.
- Mar 24, 2024: Milestone updated with detailed test, documentation, and sub-task requirements.
- Mar 28, 2024: Test cases elaborated for server permissions, global policies, and local policies. Agreed to focus on global policy tests for the Mar 29 review.
- May 8, 2024: Cognito credential provider initial implementation was too slow. Added credentials cache issue (oak-tree/medical-imaging/sonador#68) and cryptographic offline validation issue (#67 (closed)).
- May 31, 2024: Added tasks for user/group lookup via UID and integration into Orthanc.
-
Oct 24, 2024: Created follow-up Centinel milestone for overflow and additional development.
aclpermission functional tests created as #73 (closed). -
Jun 2026: Full coverage assessment completed (oak-tree/medical-imaging/sonador#64). 32 new functional tests added to
sonador-ftests/tests/covering global and local view/modify/remove/query/comment permissions, the four-role matrix across all resource types, and the MS2 worklist reassignment security scenario. Remaining gaps documented in "Remaining Gaps" above. Implemented and verified by sonador-ftests!8 (merged) (with fixes in oak-tree/medical-imaging/sonador!88 / orthanc-sonador!53 (merged) and IO-client hardening in oak-tree/medical-imaging/sonador-client!71). Documentation/diagram checklist split out to #76. -
Jul 2026:
aclpermission (Priority-1 gap) closed out — enforcement across oak-tree/medical-imaging/sonador!91 / orthanc-sonador!56 (merged) / orthanc-authorization!2 (merged), tests in sonador-ftests!13 (merged) (11/11 live). Worklist authorization decoupled from localModify(oak-tree/medical-imaging/sonador!90 / orthanc-sonador!55 (merged), tests in sonador-ftests!12 (merged)). oak-tree/medical-imaging/sonador#64 closed with a full deliverable → MR trace. Remaining scope of this issue is Priority 2–4 only (non-blocking).
Project Tracking
OP#74