mivita/app/Http/Controllers/ProfileImageController.php
Kevin e009b11259 Profilfoto: Upload nach Inhalt pruefen, Loeschen nur per POST
- Slim::storeCroppedImage() vertraute dem vom Client gemeldeten Typ und
  uebernahm die Endung aus dem Dateinamen; "shell.php" mit Typ image/png
  landete als .php im public-Disk. Jetzt entscheidet
  getimagesizefromstring() ueber die Bytes, die Endung kommt aus dem
  geprueften Typ.
- Foto loeschen war ein GET-Link und liess sich per fremdem <img src>
  ausloesen (CSRF); jetzt POST-Formular mit Token.
- Ohne Konto (users.account_id) bricht Upload/Loeschen mit 404 ab statt
  mit einem Fatal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 15:42:11 +00:00

86 lines
2.8 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Services\Slim;
use Auth;
use Illuminate\Http\RedirectResponse;
use Request;
/**
* Berater-Profilfoto (N6): Upload/Löschen über das Slim-Cropper-Muster
* ({@see UserShopController::uploadImage()}), gespeichert unter
* images/profile/ im public-Disk (user_accounts.profile_image).
*
* Der Upload ist nur mit gesetzter Einwilligungs-Checkbox möglich
* (teaminterne Anzeige in Ranglisten/Übersichten, account.profile_image_consent).
*/
class ProfileImageController extends Controller
{
public function uploadImage(): RedirectResponse
{
$user = Auth::user();
// Das Foto haengt am Konto (users.account_id). Ohne Konto gibt es nichts
// zu speichern — sonst liefe der Zugriff darunter in einen Fatal.
if (! $user->account) {
abort(404);
}
if (! Request::get('profile_image_consent')) {
\Session()->flash('alert-danger', __('account.profile_image_consent_required'));
return redirect(route('user_edit'));
}
try {
$image = Slim::getImages('images')[0];
if (isset($image['output']['data'])) {
$stored = Slim::storeCroppedImage($image, 'images/profile');
if ($stored === null) {
\Session()->flash('alert-danger', 'File is not jpg or png!');
return redirect(route('user_edit'));
}
if ($user->account->profile_image) {
\Storage::disk('public')->delete('images/profile/'.$user->account->profile_image);
}
$user->account->profile_image = $stored['filename'];
$user->account->save();
\Session()->flash('alert-success', __('msg.file_uploaded'));
return redirect(route('user_edit'));
}
\Session()->flash('alert-danger', __('msg.file_empty'));
return redirect(route('user_edit'));
} catch (\Exception $e) {
// Details ins Log, nicht ins UI — die Exception enthält Serverpfade.
\Log::warning('ProfileImageController: Upload fehlgeschlagen für User '.$user->id.': '.$e->getMessage());
\Session()->flash('alert-danger', __('msg.file_upload_failed'));
return redirect(route('user_edit'));
}
}
public function deleteImage(): RedirectResponse
{
$user = Auth::user();
if ($user->account?->profile_image) {
\Storage::disk('public')->delete('images/profile/'.$user->account->profile_image);
$user->account->profile_image = null;
$user->account->save();
\Session()->flash('alert-success', __('msg.file_deleted'));
}
return redirect(route('user_edit'));
}
}