PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.0
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
← All changes | app/Http/Controllers/UploaderController.php +91 -3 2.3.02.4.0 View file →
@@ -3,8 +3,9 @@
3 3 namespace FluentSupport\App\Http\Controllers;
4 4
5 5 use FluentSupport\App\Models\Attachment;
6 6 use FluentSupport\App\Models\Ticket;
7 +use FluentSupport\App\Modules\PermissionManager;
7 8 use FluentSupport\App\Services\EmailNotification\Settings;
8 9 use FluentSupport\App\Services\Helper;
9 10 use FluentSupport\Framework\Http\Request\Request;
10 11 use FluentSupport\App\Services\Includes\UploadService;
@@ -26,20 +27,38 @@
26 27 public function uploadTicketFiles(Request $request)
27 28 {
28 29 $settings = (new Settings())->globalBusinessSettings();
29 30 $maxFileSize = floatval($settings['max_file_size']);
31 + $maxFileUpload = intval($settings['max_file_upload']);
30 32 $mimeHeadings = Helper::getAcceptedMimeHeadings();
31 33 $maxSizeBytes = $maxFileSize * 1024;
32 34 $imageType = $request->type ? $request->type : null;
33 35
34 - $this->validateUploadedFiles($request->files(), $maxSizeBytes, $mimeHeadings, $maxFileSize);
36 + $files = $request->files();
37 +
38 + if ($partsError = $this->rejectUnexpectedFileParts($files)) {
39 + return $partsError;
40 + }
41 +
35 42 $ticketId = $this->resolveTicketId($request);
36 43 $person = $this->resolvePerson($ticketId, $request);
37 44
38 - $this->checkPermissionToUploadFile($person);
45 + if ($permissionError = $this->checkPermissionToUploadFile($person)) {
46 + return $permissionError;
47 + }
39 48
49 + if ($accessError = $this->checkTicketAccess($ticketId)) {
50 + return $accessError;
51 + }
52 +
53 + if ($quotaError = $this->checkAttachmentQuota($files, $person, $ticketId, $maxFileUpload)) {
54 + return $quotaError;
55 + }
56 +
57 + $this->validateUploadedFiles($files, $maxSizeBytes, $mimeHeadings, $maxFileSize);
58 +
40 59 try {
41 - $uploadedFiles = UploadService::handleTempFileUpload($request->files());
60 + $uploadedFiles = UploadService::handleTempFileUpload($files);
42 61 } catch (\Exception $e) {
43 62 return $this->sendError([
44 63 'message' => Helper::getSafeErrorMessage($e),
45 64 ]);
@@ -57,8 +76,73 @@
57 76 'attachments' => $attachmentHashes,
58 77 ];
59 78 }
60 79
80 + /**
81 + * Only the "file" multipart part is validated and processed downstream
82 + * (UploadService/FileSystem::put() loops every top-level part it is given), so
83 + * any other part name must be rejected here rather than silently passed through.
84 + */
85 + private function rejectUnexpectedFileParts($files)
86 + {
87 + $files = (array) $files;
88 + $unexpectedKeys = array_diff(array_keys($files), ['file']);
89 +
90 + if ($unexpectedKeys || empty($files['file'])) {
91 + return $this->sendError([
92 + 'message' => __('Invalid file upload request.', 'fluent-support'),
93 + ]);
94 + }
95 +
96 + return null;
97 + }
98 +
99 + /**
100 + * resolveTicketId() passes an agent's ticket_id through unchecked, so authorize it
101 + * before anything is written. No ticket id is legitimate — the Add Ticket form
102 + * uploads before the ticket exists.
103 + */
104 + private function checkTicketAccess($ticketId)
105 + {
106 + if (!$ticketId || !Helper::getCurrentAgent()) {
107 + return null;
108 + }
109 +
110 + $ticket = Ticket::find($ticketId);
111 +
112 + if (!$ticket || !PermissionManager::canAccessTicket($ticket)) {
113 + return $this->sendError([
114 + 'message' => __('You do not have permission to upload a file to this ticket', 'fluent-support'),
115 + ], 403);
116 + }
117 +
118 + return null;
119 + }
120 +
121 + private function checkAttachmentQuota($files, $person, $ticketId, $maxFileUpload)
122 + {
123 + if ($maxFileUpload <= 0) {
124 + return null;
125 + }
126 +
127 + $newFiles = isset($files['file']) ? $files['file'] : null;
128 + $newFilesCount = is_array($newFiles) ? count($newFiles) : 1;
129 +
130 + $existingCount = Attachment::where('person_id', $person->id)
131 + ->where('ticket_id', $ticketId)
132 + ->where('status', 'in-active')
133 + ->count();
134 +
135 + if (($existingCount + $newFilesCount) > $maxFileUpload) {
136 + return $this->sendError([
137 + // translators: %d is the maximum number of files allowed per ticket
138 + 'message' => sprintf(__('You can upload a maximum of %d files.', 'fluent-support'), $maxFileUpload),
139 + ]);
140 + }
141 +
142 + return null;
143 + }
144 +
61 145 private function validateUploadedFiles($files, $maxSizeBytes, $mimeHeadings, $maxFileSize)
62 146 {
63 147 $validationRules = [
64 148 'file' => 'max:' . $maxSizeBytes . '|mimetypes:' . implode(',', Helper::ticketAcceptedFileMiles()),
@@ -178,8 +262,12 @@
178 262 public function uploadImage(Request $request)
179 263 {
180 264 $images = $request->files();
181 265 $ticketId = $this->resolveTicketId($request);
266 +
267 + if ($accessError = $this->checkTicketAccess($ticketId)) {
268 + return $accessError;
269 + }
182 270
183 271 $validationError = $this->isValidImageType($images);
184 272 if ($validationError) {
185 273 return $validationError;