2 Commits
Author SHA1 Message Date
Joel Brock 41467bd4cf File redirect: per-entity permission check before minting fcs
Security-review follow-up to b65bc6d. The file-redirect route signs an
fcs JWT that Civi's /civicrm/file handler accepts as proof of access.
Any user with the base `access CiviCRM` permission could iterate file
IDs and have us laundering tokens past the entity-level ACLs that would
normally apply (e.g. a staff user without view permission on a given
contact could still pull files attached to that contact).

Tighten it:

- Resolve the file's linked entity_table + entity_id (was: entity_id only).
- Run the entity-type's native permission check before signing the JWT:
    civicrm_activity -> CRM_Activity_BAO_Activity::checkPermission
    civicrm_contact  -> CRM_Contact_BAO_Contact_Permission::allow
  Unknown entity types deny by default — adding a new type requires an
  explicit edit here, so we don't accidentally widen the surface.
- Drop JWT lifetime from a week to 10 minutes. The token is minted at
  click time (the user hits this route fresh on each file click), so
  the long lifetime served no purpose and made each URL a longer-lived
  bearer credential.

Files missing from civicrm_entity_file or pointing at unsupported entity
types now 403 via CRM_Utils_System::permissionDenied() instead of
producing a download URL.
2026-06-10 10:49:28 -07:00
Joel Brock b65bc6d0e0 File redirect route: mint fcs JWT in extension, link from staff report
Path 1 (APIv4 Attachment.get + select url) shipped but didn't fix the
Firebase\JWT decode crash — the deployed Civi version either omits `url`
from Attachment.get or returns it without the fcs param. Falling back to
the bare /civicrm/file?id=X URL hits the same JWT null crash.

Path 2: route file clicks through a tiny redirect endpoint in the Civi
extension instead. The extension runs PHP on Civi, has access to the
crypto.jwt service, and mints the same shape of token Civi's own file
URL builder uses ({exp, "civi.file": <id>}) before 302-redirecting to
the canonical /civicrm/file URL.

Civi extension changes:
- New CRM/WebformMw/Page/File.php — resolves eid from civicrm_entity_file
  if not supplied, signs a 7-day JWT via Civi::service('crypto.jwt'),
  redirects.
- xml/Menu/webform_mw.xml — registers civicrm/webform-mw/file. Requires
  `access CiviCRM` (the user is already authenticated in the parent Civi
  tab when they click the link).

Frontend (StaffReportView.tsx, FieldValue):
- When Attachment.get's url is missing, fall back to the new extension
  route instead of bare /civicrm/file. Attachment.get's url remains the
  fast path when present.

Deploy: admin needs to push the updated extension files to the Civi
server, then Disable/Enable webform-mw (or cv flush) so the new menu
route registers in civicrm_menu.
2026-06-10 10:47:34 -07:00