id) ->where('group_id', $ticket->group_id) ->exists(); return $isAgent || $ticket->submitter_id === $user->id; } public function store(Request $request, Ticket $ticket) { if (!$this->canAccessTicket($ticket)) { abort(403); } $request->validate([ 'file' => 'required|file|max:' . (self::MAX_SIZE_MB * 1024), // Bug #5: message_id must belong to THIS ticket, not any arbitrary message 'message_id' => [ 'nullable', 'integer', function ($attribute, $value, $fail) use ($ticket) { if ($value !== null) { $exists = \Dashboard\Ticketing\Models\TicketMessage::where('id', $value) ->where('ticket_id', $ticket->id) ->exists(); if (!$exists) { $fail('The message does not belong to this ticket.'); } } }, ], ]); $file = $request->file('file'); $disk = config('ticketing.storage_disk', env('TICKETING_STORAGE_DISK', 'local')); $path = $file->store('ticketing/attachments/' . $ticket->id, $disk); TicketAttachment::create([ 'ticket_id' => $ticket->id, 'message_id' => $request->message_id, 'filename' => $file->getClientOriginalName(), 'path' => $path, 'mime_type' => $file->getMimeType(), 'size' => $file->getSize(), ]); return back()->with('success', 'Attachment uploaded.'); } public function show(TicketAttachment $attachment) { $ticket = $attachment->ticket; if (!$this->canAccessTicket($ticket)) { abort(403); } $disk = config('ticketing.storage_disk', env('TICKETING_STORAGE_DISK', 'local')); return Storage::disk($disk)->download($attachment->path, $attachment->filename); } }