PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
2.4.5 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 All 69 releases
← All changes | app/Http/Controllers/UploaderController.php +87 -2 2.3.2 → 2.4.5 View file →
@@ -3,8 +3,10 @@
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;
8 +use FluentSupport\App\Services\CustomerPortalService;
7 9 use FluentSupport\App\Services\EmailNotification\Settings;
8 10 use FluentSupport\App\Services\Helper;
9 11 use FluentSupport\Framework\Http\Request\Request;
10 12 use FluentSupport\App\Services\Includes\UploadService;
@@ -17,8 +19,15 @@
17 19 */
18 20 class UploaderController extends Controller
19 21 {
20 22 /**
23 + * Uploads sent with on_behalf belong to that customer, and their ticket_id is the
24 + * public ticket number. Remote portals check for this constant to know which
25 + * ticket reference to send. Don't remove it.
26 + */
27 + const ON_BEHALF_UPLOADS = true;
28 +
29 + /**
21 30 * uploadTicketFiles method will upload all the attached file in a ticket
22 31 * @param Request $request
23 32 * @return array[]
24 33 * @throws \FluentSupport\Framework\Validator\ValidationException
@@ -37,15 +46,31 @@
37 46 if ($partsError = $this->rejectUnexpectedFileParts($files)) {
38 47 return $partsError;
39 48 }
40 49
41 - $ticketId = $this->resolveTicketId($request);
42 - $person = $this->resolvePerson($ticketId, $request);
50 + $onBehalf = (new CustomerPortalService())->sanitizeOnBehalf($request->get('on_behalf', []));
43 51
52 + if ($onBehalf) {
53 + try {
54 + list($ticketId, $person) = $this->resolveOnBehalfUploader($onBehalf, $request);
55 + } catch (\Exception $e) {
56 + return $this->sendError([
57 + 'message' => Helper::getSafeErrorMessage($e),
58 + ]);
59 + }
60 + } else {
61 + $ticketId = $this->resolveTicketId($request);
62 + $person = $this->resolvePerson($ticketId, $request);
63 + }
64 +
44 65 if ($permissionError = $this->checkPermissionToUploadFile($person)) {
45 66 return $permissionError;
46 67 }
47 68
69 + if ($accessError = $this->checkTicketAccess($ticketId)) {
70 + return $accessError;
71 + }
72 +
48 73 if ($quotaError = $this->checkAttachmentQuota($files, $person, $ticketId, $maxFileUpload)) {
49 74 return $quotaError;
50 75 }
51 76
@@ -90,8 +115,30 @@
90 115
91 116 return null;
92 117 }
93 118
119 + /**
120 + * resolveTicketId() passes an agent's ticket_id through unchecked, so authorize it
121 + * before anything is written. No ticket id is legitimate — the Add Ticket form
122 + * uploads before the ticket exists.
123 + */
124 + private function checkTicketAccess($ticketId)
125 + {
126 + if (!$ticketId || !Helper::getCurrentAgent()) {
127 + return null;
128 + }
129 +
130 + $ticket = Ticket::find($ticketId);
131 +
132 + if (!$ticket || !PermissionManager::canAccessTicket($ticket)) {
133 + return $this->sendError([
134 + 'message' => __('You do not have permission to upload a file to this ticket', 'fluent-support'),
135 + ], 403);
136 + }
137 +
138 + return null;
139 + }
140 +
94 141 private function checkAttachmentQuota($files, $person, $ticketId, $maxFileUpload)
95 142 {
96 143 if ($maxFileUpload <= 0) {
97 144 return null;
@@ -147,8 +194,42 @@
147 194
148 195 return $ticket ? $ticket->id : null;
149 196 }
150 197
198 + /**
199 + * A remote portal (PortalPolicy has allowed on_behalf) uploads as the customer it
200 + * acts for, so the file belongs to that customer and counts toward their quota only.
201 + * ticket_id is the public ticket number, as on every other portal endpoint.
202 + *
203 + * @return array [ticket id or null, customer]
204 + * @throws \Exception
205 + */
206 + private function resolveOnBehalfUploader($onBehalf, Request $request)
207 + {
208 + $portalService = new CustomerPortalService();
209 + $identifier = $request->getSafe('ticket_id', 'intval');
210 +
211 + if (!$identifier) {
212 + return [null, $portalService->resolveCustomer($onBehalf, $request->getIp(), true)];
213 + }
214 +
215 + $ticket = Ticket::wherePublicIdentifier($identifier)->first();
216 +
217 + if (!$ticket) {
218 + throw new \Exception(esc_html__('Sorry, You do not have permission to this support ticket', 'fluent-support'));
219 + }
220 +
221 + // getCustomer() also holds an agent to their own mailbox and ticket access.
222 + $customer = $portalService->getCustomer([
223 + 'on_behalf' => $onBehalf,
224 + 'user_ip' => $request->getIp(),
225 + ], $ticket);
226 +
227 + $portalService->checkCustomerTicketAccess($customer, $ticket, 'response');
228 +
229 + return [$ticket->id, $customer];
230 + }
231 +
151 232 private function resolvePerson($ticketId, Request $request)
152 233 {
153 234 $agent = Helper::getCurrentAgent();
154 235 if ($agent) {
@@ -235,8 +316,12 @@
235 316 public function uploadImage(Request $request)
236 317 {
237 318 $images = $request->files();
238 319 $ticketId = $this->resolveTicketId($request);
320 +
321 + if ($accessError = $this->checkTicketAccess($ticketId)) {
322 + return $accessError;
323 + }
239 324
240 325 $validationError = $this->isValidImageType($images);
241 326 if ($validationError) {
242 327 return $validationError;