feat: add routes, lang, tests, stubs, docs, and docker configurations
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
|
||||
beforeEach(function () {
|
||||
// Pest.php disables ThrottleRequests globally — re-enable for these tests.
|
||||
$this->withMiddleware(ThrottleRequests::class);
|
||||
RateLimiter::clear('login');
|
||||
});
|
||||
|
||||
test('API login throttle eventually blocks after enough failed attempts', function () {
|
||||
// The login endpoint stacks two protections: middleware throttle:10,1 and a
|
||||
// controller-level RateLimiter keyed by IP+email (configurable via
|
||||
// security_auth.login_max_attempts, default 5). After enough hits, one of
|
||||
// them must kick in and return 429.
|
||||
$sawBlock = false;
|
||||
for ($i = 0; $i < 15; $i++) {
|
||||
$r = $this->postJson('/api/v1/login', ['email' => 'x@x.com', 'password' => 'wrong']);
|
||||
if ($r->getStatusCode() === 429) {
|
||||
$sawBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect($sawBlock)->toBeTrue();
|
||||
});
|
||||
|
||||
test('API forgot-password throttle blocks after 5 requests', function () {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->postJson('/api/v1/forgot-password', ['email' => 'x@x.com']);
|
||||
}
|
||||
|
||||
$this->postJson('/api/v1/forgot-password', ['email' => 'x@x.com'])
|
||||
->assertStatus(429);
|
||||
});
|
||||
|
||||
test('API OTP send throttle blocks after 5 requests', function () {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->postJson('/api/v1/otp/send', ['email' => 'x@x.com']);
|
||||
}
|
||||
|
||||
$this->postJson('/api/v1/otp/send', ['email' => 'x@x.com'])
|
||||
->assertStatus(429);
|
||||
});
|
||||
|
||||
test('API register throttle blocks after 5 requests', function () {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->postJson('/api/v1/register', []);
|
||||
}
|
||||
|
||||
$this->postJson('/api/v1/register', [])->assertStatus(429);
|
||||
});
|
||||
|
||||
test('2FA verify throttle blocks after 5 requests', function () {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->post('/2fa', ['code' => '123456']);
|
||||
}
|
||||
|
||||
$this->post('/2fa', ['code' => '123456'])->assertStatus(429);
|
||||
});
|
||||
|
||||
test('different IPs do not share the same rate-limit bucket', function () {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->call('POST', '/api/v1/forgot-password',
|
||||
parameters: ['email' => 'x@x.com'],
|
||||
server: ['REMOTE_ADDR' => '10.0.0.1']
|
||||
);
|
||||
}
|
||||
|
||||
// Same email, different IP — should be fine
|
||||
$r = $this->call('POST', '/api/v1/forgot-password',
|
||||
parameters: ['email' => 'x@x.com'],
|
||||
server: ['REMOTE_ADDR' => '10.0.0.2']
|
||||
);
|
||||
expect($r->getStatusCode())->not->toBe(429);
|
||||
});
|
||||
Reference in New Issue
Block a user