From 1f7b06c57625b038376abbec18b15be5dd3a2dbc Mon Sep 17 00:00:00 2001 From: boban Date: Sat, 20 Jun 2026 17:26:28 +0200 Subject: [PATCH] feat(auth): BannedIp model + migration --- app/Models/BannedIp.php | 19 ++++++++++++++ ...6_06_20_000001_create_banned_ips_table.php | 25 +++++++++++++++++++ tests/Feature/BruteforceBannedIpTest.php | 22 ++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 app/Models/BannedIp.php create mode 100644 database/migrations/2026_06_20_000001_create_banned_ips_table.php create mode 100644 tests/Feature/BruteforceBannedIpTest.php diff --git a/app/Models/BannedIp.php b/app/Models/BannedIp.php new file mode 100644 index 0000000..b8f310c --- /dev/null +++ b/app/Models/BannedIp.php @@ -0,0 +1,19 @@ + 'datetime']; + + /** Non-expired bans only. */ + public function scopeActive(Builder $query): Builder + { + return $query->where('banned_until', '>', now()); + } +} diff --git a/database/migrations/2026_06_20_000001_create_banned_ips_table.php b/database/migrations/2026_06_20_000001_create_banned_ips_table.php new file mode 100644 index 0000000..9b07bf3 --- /dev/null +++ b/database/migrations/2026_06_20_000001_create_banned_ips_table.php @@ -0,0 +1,25 @@ +id(); + $table->string('ip', 45)->unique(); + $table->timestamp('banned_until'); + $table->string('reason')->nullable(); + $table->unsignedInteger('attempts')->default(0); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('banned_ips'); + } +}; diff --git a/tests/Feature/BruteforceBannedIpTest.php b/tests/Feature/BruteforceBannedIpTest.php new file mode 100644 index 0000000..150da64 --- /dev/null +++ b/tests/Feature/BruteforceBannedIpTest.php @@ -0,0 +1,22 @@ + '1.2.3.4', 'banned_until' => now()->addHour(), 'attempts' => 10]); + BannedIp::create(['ip' => '5.6.7.8', 'banned_until' => now()->subHour(), 'attempts' => 10]); + + $active = BannedIp::active()->pluck('ip')->all(); + + $this->assertSame(['1.2.3.4'], $active); + } +}