presseportale/tests/Feature/Auth/RegistrationTest.php
Kevin Adametz 5dfd622141 security: Andere Geräte abmelden, IP-weites Login-Limit, dauerhafte Sperrmarke, Passwortregeln, CSP im Melde-Modus
Restliche Phase 4 aus dem Sicherheitsaudit 07.10.2026:

- Sicherheitsseite: „Andere Geräte abmelden“ rotiert remember_token und
  beendet alle anderen Sessions, die aktuelle bleibt (nicht während einer
  Impersonation).
- LoginIpThrottle: 20 Fehlversuche je IP in 15 Minuten über alle Konten.
  Gezählt über das Failed-Event, damit Livewire-Login und Fortifys
  POST /login denselben Zähler teilen; Erfolg setzt ihn nicht zurück.
- Sperrmarke für Sessions in users.sessions_revoked_at_ms statt nur im
  Cache — übersteht cache:clear. Die alte Cache-Marke zählt übergangsweise
  mit und kann im nächsten Release entfallen.
- Neue und geänderte Passwörter: mindestens 12 Zeichen, in Produktion
  zusätzlich uncompromised() (HIBP, k-Anonymität). Bestehende bleiben gültig.
- Content-Security-Policy-Report-Only in Produktion; Meldungen über
  /csp-report nach storage/logs/csp-*.log, je Meldung höchstens einmal pro
  Stunde, ohne Query und tokenartige Pfadsegmente.

Migration: users.sessions_revoked_at_ms (users hat FULLTEXT, 25 MB, Sekunden).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:02:49 +00:00

65 lines
2.3 KiB
PHP

<?php
use App\Enums\RegistrationType;
use App\Models\User;
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Support\Facades\Notification;
use Livewire\Livewire;
use Tests\TestCase;
test('registration screen can be rendered', function () {
/** @var TestCase $this */
$response = $this->get('/register');
// Platzhalter serverseitig übersetzt, nicht als Alpine-Ausdruck
// (ReferenceError „__ is not defined“, GlitchTip 06.10.2026).
$response->assertStatus(200)
->assertSee('placeholder="Vor- und Nachname"', false)
->assertDontSee(':placeholder="__(', false);
});
test('new users register inactive and are sent to the verification notice', function () {
/** @var TestCase $this */
Notification::fake();
Livewire::test('auth.register')
->set('name', 'Test User')
->set('email', 'test@example.com')
->set('password', 'sicheres-passwort-2026')
->set('password_confirmation', 'sicheres-passwort-2026')
->set('terms_accepted', true)
->call('register')
->assertHasNoErrors()
->assertRedirect(route('verification.notice', absolute: false));
$this->assertAuthenticated();
$user = User::where('email', 'test@example.com')->firstOrFail();
// Konto bleibt bis zur Verifizierung inaktiv, rollenlos und unverifiziert.
expect($user->is_active)->toBeFalse();
expect($user->hasVerifiedEmail())->toBeFalse();
expect($user->roles()->count())->toBe(0);
// Die bestätigte AGB-/Datenschutz-Checkbox wird als Einwilligung festgehalten.
expect($user->gdpr_consent_at)->not->toBeNull();
// Selbst-Registrierungen sind Neukunden — nie der DB-Default `existing_legacy`.
expect($user->registration_type)->toBe(RegistrationType::Company);
Notification::assertSentTo($user, VerifyEmail::class);
});
test('registration requires accepting the terms', function () {
/** @var TestCase $this */
Livewire::test('auth.register')
->set('name', 'Test User')
->set('email', 'terms@example.com')
->set('password', 'sicheres-passwort-2026')
->set('password_confirmation', 'sicheres-passwort-2026')
->set('terms_accepted', false)
->call('register')
->assertHasErrors(['terms_accepted']);
expect(User::where('email', 'terms@example.com')->exists())->toBeFalse();
});