'Pia', 'last_name' => 'Probe', ]); return User::forceCreate([ 'email' => 'pi-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'lang' => 'de', 'm_level' => 1, 'account_id' => $account->id, 'payment_account' => now()->addYear(), ]); } /** Echte Bytes eines 1x1-JPEG bzw. -PNG — storeCroppedImage() prueft den Inhalt, nicht die Client-Angabe. */ function piImageBytes(string $type): string { $base64 = $type === 'png' ? 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==' : '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6ery8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APP6KKKAP//Z'; return base64_decode($base64); } it('verweigert den Foto-Upload ohne Einwilligung', function (): void { URL::resolveMissingNamedRoutesUsing(fn (string $name, $parameters = []) => '/'.$name); Storage::fake('public'); $user = piMakeUser(); $this->actingAs($user); Request::merge(['profile_image_consent' => null]); app(ProfileImageController::class)->uploadImage(); expect(session('alert-danger'))->toBe(__('account.profile_image_consent_required')) ->and($user->account->fresh()->profile_image)->toBeNull(); }); it('speichert ein Slim-Upload nur fuer jpg/png und liefert die Metadaten', function (): void { Storage::fake('public'); $image = [ 'input' => ['size' => 1234], 'output' => ['data' => piImageBytes('jpg'), 'name' => 'Mein Foto!.jpg', 'type' => 'image/jpeg'], ]; $stored = Slim::storeCroppedImage($image, 'images/profile'); expect($stored)->not->toBeNull() ->and($stored['ext'])->toBe('jpg') ->and($stored['mime'])->toBe('image/jpeg') ->and($stored['size'])->toBe(1234); Storage::disk('public')->assertExists('images/profile/'.$stored['filename']); $gif = ['input' => [], 'output' => ['data' => 'X', 'name' => 'a.gif', 'type' => 'image/gif']]; expect(Slim::storeCroppedImage($gif, 'images/profile'))->toBeNull(); }); it('legt die Datei-Endung nach dem geprueften Inhalt an, nicht nach dem Dateinamen', function (): void { // Angriffsbild: der Client deklariert image/png, nennt die Datei aber .php. // Frueher landete genau dieser Name im oeffentlichen Disk. Storage::fake('public'); $image = [ 'input' => ['size' => 10], 'output' => ['data' => piImageBytes('png'), 'name' => 'shell.php', 'type' => 'image/png'], ]; $stored = Slim::storeCroppedImage($image, 'images/profile'); expect($stored)->not->toBeNull() ->and($stored['filename'])->toEndWith('.png') ->and($stored['filename'])->not->toContain('.php'); Storage::disk('public')->assertExists('images/profile/'.$stored['filename']); }); it('lehnt einen Upload ab, dessen Inhalt kein Bild ist', function (): void { // Die Typ-Angabe kommt aus dem geposteten JSON und ist damit frei waehlbar — // massgeblich sind die Bytes. Storage::fake('public'); $image = [ 'input' => ['size' => 10], 'output' => ['data' => ' 'foto.png', 'type' => 'image/png'], ]; expect(Slim::storeCroppedImage($image, 'images/profile'))->toBeNull(); expect(Storage::disk('public')->allFiles('images/profile'))->toBe([]); }); it('loescht das Profilfoto samt Datei', function (): void { URL::resolveMissingNamedRoutesUsing(fn (string $name, $parameters = []) => '/'.$name); Storage::fake('public'); $user = piMakeUser(); Storage::disk('public')->put('images/profile/test_pic.jpg', 'BINARY'); $user->account->forceFill(['profile_image' => 'test_pic.jpg'])->save(); $this->actingAs($user); app(ProfileImageController::class)->deleteImage(); expect($user->account->fresh()->profile_image)->toBeNull(); Storage::disk('public')->assertMissing('images/profile/test_pic.jpg'); });