You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
5.0 KiB
134 lines
5.0 KiB
<?php
|
|
|
|
namespace Tests\Unit\Services;
|
|
|
|
use App\Models\Admin;
|
|
use App\Services\AdminSmsChallengeService;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Tests\TestCase;
|
|
|
|
class AdminSmsChallengeServiceTest extends TestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
config([
|
|
'app.key' => 'test-key',
|
|
'admin-sms.cache_store' => 'array',
|
|
]);
|
|
Cache::store('array')->flush();
|
|
}
|
|
|
|
public function test_challenge_is_six_digits_hashed_and_single_use(): void
|
|
{
|
|
$service = app(AdminSmsChallengeService::class);
|
|
$sentCode = null;
|
|
$challengeId = $service->issueChallenge('13800138000', '127.0.0.1', function ($code) use (&$sentCode) {
|
|
$sentCode = $code;
|
|
return true;
|
|
});
|
|
|
|
$this->assertNotNull($challengeId);
|
|
$this->assertMatchesRegularExpression('/^\d{6}$/', $sentCode);
|
|
$stored = Cache::store('array')->get($service->challengeKey($challengeId));
|
|
$this->assertArrayNotHasKey('code', $stored);
|
|
$this->assertNotSame($sentCode, $stored['code_hash']);
|
|
$this->assertTrue(Hash::check($sentCode, $stored['code_hash']));
|
|
|
|
$admin = new Admin();
|
|
$admin->forceFill(['id' => 7, 'mobile' => '13800138000']);
|
|
$success = $service->verify($challengeId, '13800138000', $sentCode, '127.0.0.1', $admin);
|
|
$replay = $service->verify($challengeId, '13800138000', $sentCode, '127.0.0.1', $admin);
|
|
|
|
$this->assertTrue($success['ok']);
|
|
$this->assertFalse($replay['ok']);
|
|
}
|
|
|
|
public function test_fifth_failed_attempt_locks_and_invalidates_challenge(): void
|
|
{
|
|
$service = app(AdminSmsChallengeService::class);
|
|
$challengeId = $service->issueChallenge('13800138000', '127.0.0.1', fn () => true);
|
|
$admin = new Admin();
|
|
$admin->forceFill(['id' => 7, 'mobile' => '13800138000']);
|
|
|
|
$result = null;
|
|
for ($attempt = 0; $attempt < 5; $attempt++) {
|
|
$result = $service->verify($challengeId, '13800138000', '000000', '127.0.0.1', $admin);
|
|
}
|
|
|
|
$this->assertFalse($result['ok']);
|
|
$this->assertTrue($result['locked']);
|
|
$this->assertNull(Cache::store('array')->get($service->challengeKey($challengeId)));
|
|
}
|
|
|
|
public function test_phone_send_limit_allows_one_request_per_minute(): void
|
|
{
|
|
$service = app(AdminSmsChallengeService::class);
|
|
|
|
$this->assertTrue($service->canSend('13800138000'));
|
|
$this->assertFalse($service->canSend('13800138000'));
|
|
}
|
|
|
|
public function test_ip_limit_applies_across_multiple_mobile_numbers(): void
|
|
{
|
|
config([
|
|
'admin-sms.ip_max_failures' => 2,
|
|
'admin-sms.phone_max_failures' => 10,
|
|
]);
|
|
$service = app(AdminSmsChallengeService::class);
|
|
$admin = new Admin();
|
|
$admin->forceFill(['id' => 7, 'mobile' => '13800138000']);
|
|
|
|
foreach (['13800138000', '13900139000'] as $mobile) {
|
|
$challengeId = $service->issueChallenge($mobile, '127.0.0.1', fn () => true);
|
|
$result = $service->verify($challengeId, $mobile, '000000', '127.0.0.1', $mobile === $admin->mobile ? $admin : null);
|
|
$this->assertFalse($result['ok']);
|
|
}
|
|
|
|
$third = $service->issueChallenge('13700137000', '127.0.0.1', fn () => true);
|
|
$result = $service->verify($third, '13700137000', '000000', '127.0.0.1', null);
|
|
$this->assertTrue($result['locked']);
|
|
}
|
|
|
|
public function test_account_lock_applies_across_challenges(): void
|
|
{
|
|
config([
|
|
'admin-sms.account_max_failures' => 2,
|
|
'admin-sms.phone_max_failures' => 10,
|
|
'admin-sms.ip_max_failures' => 30,
|
|
]);
|
|
$service = app(AdminSmsChallengeService::class);
|
|
$admin = new Admin();
|
|
$admin->forceFill(['id' => 8, 'mobile' => '13800138000']);
|
|
|
|
for ($attempt = 0; $attempt < 2; $attempt++) {
|
|
$challengeId = $service->issueChallenge('13800138000', '127.0.0.' . ($attempt + 1), fn () => true);
|
|
$result = $service->verify($challengeId, '13800138000', '000000', '127.0.0.' . ($attempt + 1), $admin);
|
|
}
|
|
|
|
$this->assertTrue($result['locked']);
|
|
$third = $service->issueChallenge('13800138000', '127.0.0.3', fn () => true);
|
|
$result = $service->verify($third, '13800138000', '000000', '127.0.0.3', $admin);
|
|
$this->assertTrue($result['locked']);
|
|
}
|
|
|
|
public function test_production_sms_sending_is_restricted_to_configured_ips(): void
|
|
{
|
|
config([
|
|
'admin-sms.allowed_ips' => ['10.0.0.8'],
|
|
]);
|
|
$this->app->instance('env', 'production');
|
|
|
|
$service = app(AdminSmsChallengeService::class);
|
|
|
|
$this->assertFalse($service->canSend('13800138000', '10.0.0.9'));
|
|
$this->assertTrue($service->canSend('13800138000', '10.0.0.8'));
|
|
|
|
config(['admin-sms.allowed_ips' => []]);
|
|
$this->assertTrue($service->canSend('13900139000', '10.0.0.9'));
|
|
|
|
$this->app->instance('env', 'testing');
|
|
}
|
|
}
|