seedScenarioIfMissing('admin_login_alert'); } private function login(string $username, string $password) { return $this->postJson('/api/v1/administrator/login', ['username' => $username, 'password' => $password]); } private function staff(): User { return User::factory()->create(['username' => 'staff', 'password' => Hash::make('correct-password-123')]); } public function test_ip_is_blocked_for_20_minutes_after_more_than_4_failures(): void { $this->staff(); for ($i = 4; $i >= 1; $i--) { $this->login('staff', 'wrong')->assertUnauthorized()->assertJsonPath('remaining_attempts', $i); } $this->login('staff', 'wrong')->assertStatus(429); $this->login('staff', 'correct-password-123')->assertStatus(429); $this->login('someone-else', 'whatever')->assertStatus(429); $this->travel(21)->minutes(); $this->login('staff', 'correct-password-123')->assertOk(); $this->assertSame(0, AdminLoginBlock::count()); } public function test_successful_login_alerts_configured_recipients(): void { $this->staff(); $this->login('staff', 'correct-password-123')->assertOk(); Mail::assertQueued(AdminLoginAlertMail::class, fn ($mail) => $mail->hasTo('web@tgs.com.vn')); Setting::updateOrCreate(['key' => 'email_settings'], ['value' => json_encode([AdminLoginAlert::SETTING_FIELD => ['it@tgs.com.vn']])]); $this->login('staff', 'correct-password-123')->assertOk(); Mail::assertQueued(AdminLoginAlertMail::class, fn ($mail) => $mail->hasTo('it@tgs.com.vn')); } public function test_empty_recipient_list_disables_alert(): void { $this->staff(); Setting::updateOrCreate(['key' => 'email_settings'], ['value' => json_encode([AdminLoginAlert::SETTING_FIELD => []])]); $this->login('staff', 'correct-password-123')->assertOk(); Mail::assertNothingQueued(); } public function test_admin_can_list_and_unblock_ip(): void { $this->staff(); for ($i = 0; $i < 5; $i++) { $this->login('staff', 'wrong'); } Role::firstOrCreate(['name' => 'Full Administrator', 'guard_name' => 'admin']); $admin = User::factory()->create(); $admin->assignRole('Full Administrator'); $id = $this->actingAs($admin, 'admin')->getJson('/api/v1/administrator/login-blocks') ->assertOk() ->assertJsonPath('data.items.0.ip', '127.0.0.1') ->assertJsonPath('data.items.0.last_username', 'staff') ->json('data.items.0.id'); $this->actingAs($admin, 'admin')->deleteJson("/api/v1/administrator/login-blocks/{$id}")->assertOk(); $this->app['auth']->forgetGuards(); $this->login('staff', 'correct-password-123')->assertOk(); } }