From 3838d052f06803f148f01dcf1309aa5d856a67e0 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Wed, 15 Jul 2026 18:35:32 -0400 Subject: [PATCH] Add secure file handler for client ticket attachment and file view along with client and contact permission isolation, add the handler links in asset and contact --- agent/asset.php | 3 +- agent/contact.php | 3 +- agent/document.php | 2 +- .../asset/{asset_details.php => asset.php} | 9 +- .../{contact_details.php => contact.php} | 6 +- client/document.php | 20 +-- client/file.php | 131 ++++++++++++++++++ client/ticket.php | 34 ++++- client/ticket_attachment.php | 117 ++++++++++++++++ 9 files changed, 300 insertions(+), 25 deletions(-) rename agent/modals/asset/{asset_details.php => asset.php} (99%) rename agent/modals/contact/{contact_details.php => contact.php} (99%) create mode 100644 client/file.php create mode 100644 client/ticket_attachment.php diff --git a/agent/asset.php b/agent/asset.php index 8e2bc52b..bd1c487c 100644 --- a/agent/asset.php +++ b/agent/asset.php @@ -941,7 +941,6 @@ if (isset($_GET['asset_id'])) { $file_id = intval($row['file_id']); $file_name = escapeHtml($row['file_name']); $file_description = escapeHtml($row['file_description']); - $file_reference_name = escapeHtml($row['file_reference_name']); $file_ext = escapeHtml($row['file_ext']); if ($file_ext == 'pdf') { $file_icon = "file-pdf"; @@ -972,7 +971,7 @@ if (isset($_GET['asset_id'])) { ?> - " target="_blank" >$file_description"; ?> + $file_description"; ?> diff --git a/agent/contact.php b/agent/contact.php index a8411f2b..5031c1ae 100644 --- a/agent/contact.php +++ b/agent/contact.php @@ -1047,7 +1047,6 @@ if (isset($_GET['contact_id'])) { $file_description = escapeHtml($row['file_description']); $file_size = escapeHtml($row['file_size']); $file_size_KB = round($file_size / 1024); - $file_reference_name = escapeHtml($row['file_reference_name']); $file_mime_type = escapeHtml($row['file_mime_type']); $file_created_at = escapeHtml($row['file_created_at']); @@ -1057,7 +1056,7 @@ if (isset($_GET['contact_id'])) { -
+
diff --git a/agent/document.php b/agent/document.php index 48a97bf2..f367f032 100644 --- a/agent/document.php +++ b/agent/document.php @@ -225,7 +225,7 @@ $page_title = $row['document_name']; ?>
- + diff --git a/agent/modals/asset/asset_details.php b/agent/modals/asset/asset.php similarity index 99% rename from agent/modals/asset/asset_details.php rename to agent/modals/asset/asset.php index ff0b6919..25fa7f3d 100644 --- a/agent/modals/asset/asset_details.php +++ b/agent/modals/asset/asset.php @@ -220,8 +220,6 @@ if (isset($_GET['client_id'])) { $client_url = ''; } -enforceClientAccess(); - ob_start(); ?> @@ -863,7 +861,6 @@ ob_start(); $file_name = escapeHtml($row['file_name']); $file_mime_type = escapeHtml($row['file_mime_type']); $file_description = escapeHtml($row['file_description']); - $file_reference_name = escapeHtml($row['file_reference_name']); $file_ext = escapeHtml($row['file_ext']); if ($file_ext == 'pdf') { $file_icon = "file-pdf"; @@ -891,7 +888,11 @@ ob_start(); $file_created_at = escapeHtml($row['file_created_at']); ?> - " target="_blank" >$file_description"; ?> + + + $file_description"; ?> + + diff --git a/agent/modals/contact/contact_details.php b/agent/modals/contact/contact.php similarity index 99% rename from agent/modals/contact/contact_details.php rename to agent/modals/contact/contact.php index b6ac17da..f4695ea3 100644 --- a/agent/modals/contact/contact_details.php +++ b/agent/modals/contact/contact.php @@ -821,11 +821,11 @@ ob_start(); -
+
+ +
diff --git a/client/document.php b/client/document.php index 3f639b75..ae6384c0 100644 --- a/client/document.php +++ b/client/document.php @@ -76,13 +76,13 @@ if (mysqli_num_rows($sql_files) > 0) { $file_row = mysqli_fetch_assoc($sql_files); $file_id = intval($file_row['file_id']); $file_name = escapeHtml($file_row['file_name']); - $file_reference_name = escapeHtml($file_row['file_reference_name']); $file_ext = strtolower($file_row['file_ext']); $file_size = intval($file_row['file_size']); $file_mime_type = escapeHtml($file_row['file_mime_type']); $file_size_formatted = formatBytes($file_size); - $file_path = "../uploads/clients/$session_client_id/$file_reference_name"; + $file_view_url = "file.php?file_id=$file_id&action=view"; + $file_download_url = "file.php?file_id=$file_id"; // For PDF files, display them inline if ($file_ext == 'pdf') { @@ -97,17 +97,17 @@ if (mysqli_num_rows($sql_files) > 0) {
- + Open in New Tab - + Download
- +
0) {
- + View Full Size - + Download
- <?php echo $file_name; ?> + <?php echo $file_name; ?>
0) {
- + Open File - + Download
diff --git a/client/file.php b/client/file.php new file mode 100644 index 00000000..091352e1 --- /dev/null +++ b/client/file.php @@ -0,0 +1,131 @@ += $file_mtime) +) { + http_response_code(304); + exit; +} + +// Audit log +logAudit("File", "Download", "Client contact $session_contact_name viewed file $file_name_escaped via portal", $session_client_id); + +// Send the file +header("Content-Type: $file_mime_type"); +header("Content-Disposition: $disposition; filename=\"$safe_file_name\""); +header("Content-Length: " . filesize($file_path)); +header("X-Content-Type-Options: nosniff"); + +// Clear output buffers so large files stream instead of loading into memory +while (ob_get_level()) { + ob_end_clean(); +} + +readfile($file_path); +exit; \ No newline at end of file diff --git a/client/ticket.php b/client/ticket.php index 66e8b627..b5a5a69a 100644 --- a/client/ticket.php +++ b/client/ticket.php @@ -127,13 +127,26 @@ if (isset($_GET['id']) && intval($_GET['id'])) {
+ + $name | Download | View"; + + ?> + + + + + + + +
+ [View][Download] +
@@ -319,13 +332,26 @@ if (isset($_GET['id']) && intval($_GET['id'])) {
+ + $name | Download | View"; + + ?> + + + + + + + +
+ [View][Download] +
diff --git a/client/ticket_attachment.php b/client/ticket_attachment.php new file mode 100644 index 00000000..3d20e994 --- /dev/null +++ b/client/ticket_attachment.php @@ -0,0 +1,117 @@ += $file_mtime) +) { + http_response_code(304); + exit; +} + +// Audit log +logAudit("Ticket", "Download", "Client contact $session_contact_name viewed ticket attachment $attachment_name_escaped via portal", $session_client_id); + +// Send the file +header("Content-Type: $attachment_mime_type"); +header("Content-Disposition: $disposition; filename=\"$safe_attachment_name\""); +header("Content-Length: " . filesize($file_path)); +header("X-Content-Type-Options: nosniff"); + +// Clear output buffers so large files stream instead of loading into memory +while (ob_get_level()) { + ob_end_clean(); +} + +readfile($file_path); +exit; \ No newline at end of file