feat: enhance player authentication and agent management features
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
- Updated AGENTS.md to clarify player interface bindings and agent account restrictions. - Improved PlayerAuthLoginController to include captcha verification for player login. - Enhanced AdminPlayerIndexController with permission checks for admin users. - Refactored AdminPlayerStoreController to enforce agent node restrictions for non-super admins. - Introduced new error codes for player authentication failures and updated related services. - Enhanced validation rules for agent profiles to include settlement cycle options. - Improved AdminCaptchaService to support separate scopes for admin and player captcha handling. - Updated various services to ensure proper credit management and settlement processes.
This commit is contained in:
@@ -6,8 +6,10 @@ use App\Support\PlayerFundingMode;
|
||||
use Database\Seeders\CurrencySeeder;
|
||||
use Database\Seeders\LotterySettingsSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
@@ -21,6 +23,24 @@ beforeEach(function (): void {
|
||||
$this->seed(LotterySettingsSeeder::class);
|
||||
});
|
||||
|
||||
/**
|
||||
* @return array{captcha_key: string, captcha_code: string}
|
||||
*/
|
||||
function playerLoginCaptcha(string $code = 'xwz2'): array
|
||||
{
|
||||
$key = (string) Str::uuid();
|
||||
Cache::put(
|
||||
'player_captcha:'.$key,
|
||||
hash_hmac('sha256', strtolower($code), (string) config('app.key')),
|
||||
now()->addSeconds(120),
|
||||
);
|
||||
|
||||
return [
|
||||
'captcha_key' => $key,
|
||||
'captcha_code' => $code,
|
||||
];
|
||||
}
|
||||
|
||||
test('native player can login without site code using default site', function (): void {
|
||||
$site = DB::table('admin_sites')->where('is_default', true)->first();
|
||||
$rootId = (int) DB::table('agent_nodes')->where('depth', 0)->value('id');
|
||||
@@ -38,10 +58,10 @@ test('native player can login without site code using default site', function ()
|
||||
'status' => 0,
|
||||
]);
|
||||
|
||||
$login = $this->postJson('/api/v1/player/auth/login', [
|
||||
$login = $this->postJson('/api/v1/player/auth/login', array_merge([
|
||||
'username' => 'agentplayer0',
|
||||
'password' => 'secret-pass',
|
||||
]);
|
||||
], playerLoginCaptcha()));
|
||||
|
||||
$login->assertOk()
|
||||
->assertJsonPath('data.player.id', $player->id);
|
||||
@@ -81,10 +101,10 @@ test('native player can login without site code on non-default site when usernam
|
||||
'status' => 0,
|
||||
]);
|
||||
|
||||
$this->postJson('/api/v1/player/auth/login', [
|
||||
$this->postJson('/api/v1/player/auth/login', array_merge([
|
||||
'username' => 'play1',
|
||||
'password' => 'secret-pass',
|
||||
])
|
||||
], playerLoginCaptcha()))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.player.id', $player->id)
|
||||
->assertJsonPath('data.player.site_code', 'kk88');
|
||||
@@ -132,10 +152,47 @@ test('native player login without site code rejects ambiguous username across si
|
||||
]);
|
||||
}
|
||||
|
||||
$this->postJson('/api/v1/player/auth/login', [
|
||||
$this->postJson('/api/v1/player/auth/login', array_merge([
|
||||
'username' => 'dup_user',
|
||||
'password' => 'secret-pass',
|
||||
])->assertJsonPath('code', 8006);
|
||||
], playerLoginCaptcha()))->assertJsonPath('code', 8006);
|
||||
});
|
||||
|
||||
test('player auth captcha exposes key and image base64', function (): void {
|
||||
$resp = $this->getJson('/api/v1/player/auth/captcha');
|
||||
|
||||
$resp->assertOk();
|
||||
$data = $resp->json('data');
|
||||
expect($data)->toHaveKeys(['captcha_key', 'image_base64'])
|
||||
->and(Str::isUuid((string) $data['captcha_key']))->toBeTrue()
|
||||
->and(base64_decode((string) $data['image_base64'], true))->not->toBeFalse();
|
||||
});
|
||||
|
||||
test('native player login rejects invalid captcha', function (): void {
|
||||
$site = DB::table('admin_sites')->where('is_default', true)->first();
|
||||
$rootId = (int) DB::table('agent_nodes')->where('depth', 0)->value('id');
|
||||
|
||||
Player::query()->create([
|
||||
'site_code' => (string) $site->code,
|
||||
'agent_node_id' => $rootId,
|
||||
'site_player_id' => 'native:captcha-fail',
|
||||
'auth_source' => PlayerAuthSource::LOTTERY_NATIVE,
|
||||
'funding_mode' => PlayerFundingMode::CREDIT,
|
||||
'username' => 'captchafail',
|
||||
'password_hash' => Hash::make('secret-pass'),
|
||||
'nickname' => null,
|
||||
'default_currency' => 'NPR',
|
||||
'status' => 0,
|
||||
]);
|
||||
|
||||
$captcha = playerLoginCaptcha('xwz2');
|
||||
|
||||
$this->postJson('/api/v1/player/auth/login', [
|
||||
'username' => 'captchafail',
|
||||
'password' => 'secret-pass',
|
||||
'captcha_key' => $captcha['captcha_key'],
|
||||
'captcha_code' => 'aaaa',
|
||||
])->assertStatus(422)->assertJsonPath('code', 8009);
|
||||
});
|
||||
|
||||
test('native player can login and access me', function (): void {
|
||||
@@ -164,11 +221,11 @@ test('native player can login and access me', function (): void {
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$login = $this->postJson('/api/v1/player/auth/login', [
|
||||
$login = $this->postJson('/api/v1/player/auth/login', array_merge([
|
||||
'site_code' => $site->code,
|
||||
'username' => 'agentplayer1',
|
||||
'password' => 'secret-pass',
|
||||
]);
|
||||
], playerLoginCaptcha()));
|
||||
|
||||
$login->assertOk();
|
||||
$token = (string) $login->json('data.access_token');
|
||||
|
||||
Reference in New Issue
Block a user