- Shipped
- September 23, 2026 at 2:08 AM UTC
- Author
- Kamo
- Commit
- 01ee8eb
MediaService/telephony store chat attachments (CHAT_ATTACHMENT) and voicemail/call recordings (VM_RECORDING, CALL_RECORDING) at ACCESS_LEVEL 0. Every generic imaging path (/download, /stream, /bulk-download, /list, /delete) authorizes on **************** org+clearance check alone, so any org member holding VIEW_DOCUMENTS could read, export or list any colleague's chat attachment, voicemail or call recording — outside the conversation it was shared in, past a revoked attachment (MediaObjImg.isAccessRevoked), and regardless of whose recording it was. /delete additionally never read ImageAssocType.isAllowDelete at all: every category, including PATIENT_CHART and CATALOG_SOURCE (both hold isAllowDelete=false specifically because deleting them destroys evidence a disclosure accounting or a published rate sheet's provenance depends on), could be soft-deleted through this one endpoint regardless of its own rule. New MediaAssocAccess (kamo-shared-library's findByImg_Id, pushed separately as a prerequisite) adds the narrower rule these three association types were always missing: a CHAT_ATTACHMENT requires membership in the MediaSession named by assocObjectId (and honours isAccessRevoked); a VM_RECORDING/CALL_RECORDING requires ownership (createdByMember) or MANAGE_VOIP_SETTINGS — the right the Phone Settings admin page already gates its cross-extension voicemail view behind. Wired into every read/export/list/delete path; /list also drops the three types from an unfiltered "browse everything" query in DocumentListQuery (a caller that explicitly asks for one by assocType still gets it, filtered per-row). /delete now also resolves the effective isAllowDelete (ImageAssocType default, ImgOrgAssoc org-wide override) before touching a row, read directly via imgRepository rather than getDocumentMetadata — deleting was deliberately never subject to the clearance ceiling that read imposes, and this preserves that. New tests: MediaAssocAccessTest (11 cases, the guard's own logic) and ImagingControllerMediaAssocTest (9 cases, proving every controller call site actually invokes it). Mutation-checked: forcing MediaAssocAccess#denialReason to always return null turns 5 of MediaAssocAccessTest's cases red; reverting the controller/DocumentListQuery wiring to origin/main breaks the build outright (the new constructor argument is required), which is failure by construction rather than by assertion. Five pre-existing ImagingController tests updated to pass the new Docstor (real instance where the endpoint under test now reaches it; null where it provably does not). Full suite: 725 tests green (was 705; +20 new).
