135 lines
4.3 KiB
PHP
135 lines
4.3 KiB
PHP
<?php
|
|
|
|
use App\Models\AuthAuditLog;
|
|
use App\Models\User;
|
|
use Illuminate\Auth\Notifications\ResetPassword;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Notification;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Inertia\Testing\AssertableInertia;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('logs successful login attempts', function (): void {
|
|
$user = User::factory()->create();
|
|
|
|
$this->post('/login', [
|
|
'email' => $user->email,
|
|
'password' => 'password',
|
|
])->assertRedirect(route('dashboard', absolute: false));
|
|
|
|
$log = AuthAuditLog::query()->latest('id')->first();
|
|
|
|
expect($log)->not->toBeNull()
|
|
->and($log->event_type)->toBe('login')
|
|
->and($log->status)->toBe('success')
|
|
->and($log->identifier)->toBe(strtolower($user->email))
|
|
->and($log->user_id)->toBe($user->id);
|
|
});
|
|
|
|
it('logs login validation failures', function (): void {
|
|
config()->set('app.debug', false);
|
|
|
|
$this->from('/login')->post('/login', [
|
|
'email' => '',
|
|
'password' => '',
|
|
])->assertRedirect('/login')
|
|
->assertSessionHasErrors(['email', 'password']);
|
|
|
|
$log = AuthAuditLog::query()->latest('id')->first();
|
|
|
|
expect($log)->not->toBeNull()
|
|
->and($log->event_type)->toBe('login')
|
|
->and($log->status)->toBe('failed')
|
|
->and($log->reason)->toBe('validation_failed')
|
|
->and($log->metadata)->toMatchArray(['fields' => ['email', 'password']]);
|
|
});
|
|
|
|
it('logs successful registration attempts', function (): void {
|
|
Queue::fake();
|
|
|
|
$this->post('/register', [
|
|
'email' => 'audit-register@example.com',
|
|
])->assertRedirect(route('setup.password.create', absolute: false));
|
|
|
|
$log = AuthAuditLog::query()->where('event_type', 'register')->latest('id')->first();
|
|
|
|
expect($log)->not->toBeNull()
|
|
->and($log->status)->toBe('success')
|
|
->and($log->identifier)->toBe('audit-register@example.com')
|
|
->and($log->reason)->toBe('user_created');
|
|
});
|
|
|
|
it('logs forgot password attempts', function (): void {
|
|
Notification::fake();
|
|
|
|
$user = User::factory()->create();
|
|
|
|
$this->post('/forgot-password', ['email' => $user->email])
|
|
->assertSessionHas('status');
|
|
|
|
$log = AuthAuditLog::query()->where('event_type', 'forgot_password')->latest('id')->first();
|
|
|
|
expect($log)->not->toBeNull()
|
|
->and($log->status)->toBe('success')
|
|
->and($log->identifier)->toBe(strtolower($user->email))
|
|
->and($log->user_id)->toBe($user->id);
|
|
});
|
|
|
|
it('logs successful password resets', function (): void {
|
|
Notification::fake();
|
|
|
|
$user = User::factory()->create();
|
|
|
|
$this->post('/forgot-password', ['email' => $user->email]);
|
|
|
|
$token = null;
|
|
|
|
Notification::assertSentTo($user, ResetPassword::class, function (ResetPassword $notification) use (&$token): bool {
|
|
$token = $notification->token;
|
|
|
|
return true;
|
|
});
|
|
|
|
$this->post('/reset-password', [
|
|
'token' => $token,
|
|
'email' => $user->email,
|
|
'password' => 'password',
|
|
'password_confirmation' => 'password',
|
|
])->assertRedirect(route('login'));
|
|
|
|
$log = AuthAuditLog::query()->where('event_type', 'reset_password')->latest('id')->first();
|
|
|
|
expect($log)->not->toBeNull()
|
|
->and($log->status)->toBe('success')
|
|
->and($log->identifier)->toBe(strtolower($user->email))
|
|
->and($log->user_id)->toBe($user->id);
|
|
});
|
|
|
|
it('limits the auth audit moderation page to admins', function (): void {
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$manager = User::factory()->create(['role' => 'manager']);
|
|
|
|
AuthAuditLog::query()->create([
|
|
'event_type' => 'login',
|
|
'identifier' => 'audit@example.com',
|
|
'status' => 'failed',
|
|
'reason' => 'invalid_credentials',
|
|
'ip' => '127.0.0.1',
|
|
'metadata' => ['via' => 'email'],
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
$this->actingAs($admin)
|
|
->get('/moderation/auth-audit')
|
|
->assertOk()
|
|
->assertInertia(fn (AssertableInertia $page) => $page
|
|
->component('Admin/AuthAudit')
|
|
->where('logs.data.0.event_type', 'login')
|
|
->where('logs.data.0.reason', 'invalid_credentials')
|
|
);
|
|
|
|
$this->actingAs($manager)
|
|
->get('/moderation/auth-audit')
|
|
->assertForbidden();
|
|
}); |