fix(core): harden settlement and wallet integration
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
<?php
|
||||
|
||||
use App\Models\AdminSite;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\AdminUser;
|
||||
use App\Models\Player;
|
||||
use App\Services\Integration\PartnerSiteConfig;
|
||||
use App\Services\Integration\PartnerSiteConfigResolver;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\AdminSite;
|
||||
use App\Models\AdminUser;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Database\Seeders\CurrencySeeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Services\Integration\PartnerSiteConfig;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use App\Services\Integration\PartnerSiteConfigResolver;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function (): void {
|
||||
fakeWalletApiDns();
|
||||
$this->artisan('lottery:admin-auth-sync')->assertExitCode(0);
|
||||
});
|
||||
|
||||
@@ -221,7 +223,81 @@ test('connectivity test probes partner balance api', function (): void {
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('data.probe.success', true)
|
||||
->assertJsonPath('data.probe.main_balance_minor', 12345);
|
||||
->assertJsonPath('data.probe.main_balance_minor', 12345)
|
||||
->assertJsonMissingPath('data.probe.response_preview');
|
||||
});
|
||||
|
||||
test('connectivity test rejects hostname resolving to a private address before sending', function (): void {
|
||||
fakeWalletApiDns([
|
||||
'wallet.private.test' => ['93.184.216.34', '10.0.0.8'],
|
||||
], []);
|
||||
Http::preventStrayRequests();
|
||||
|
||||
$token = integrationAdminToken();
|
||||
$create = $this->withHeader('Authorization', 'Bearer '.$token)
|
||||
->postJson('/api/v1/admin/integration-sites', [
|
||||
'code' => 'private-probe-site',
|
||||
'name' => 'Private Probe',
|
||||
'wallet_api_url' => 'https://wallet.private.test',
|
||||
'admin_account' => [
|
||||
'username' => 'private_probe_admin',
|
||||
'nickname' => 'Private Probe Admin',
|
||||
'password' => 'secret-strong',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->withHeader('Authorization', 'Bearer '.$token)
|
||||
->postJson('/api/v1/admin/integration-sites/'.(int) $create->json('data.id').'/connectivity-test', [
|
||||
'site_player_id' => '10001',
|
||||
'currency_code' => 'NPR',
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.probe.success', false)
|
||||
->assertJsonPath('data.probe.http_status', null)
|
||||
->assertJsonPath('data.probe.message', 'wallet_api_url 无效(拒绝以防 SSRF)')
|
||||
->assertJsonMissingPath('data.probe.response_preview');
|
||||
|
||||
Http::assertSentCount(0);
|
||||
});
|
||||
|
||||
test('connectivity test does not follow redirects or expose response preview', function (): void {
|
||||
fakeWalletApiDns([
|
||||
'wallet.redirect.test' => ['93.184.216.34'],
|
||||
], []);
|
||||
Http::fake([
|
||||
'https://wallet.redirect.test/*' => Http::response(
|
||||
['secret' => 'must-not-be-returned'],
|
||||
302,
|
||||
['Location' => 'https://169.254.169.254/latest/meta-data'],
|
||||
),
|
||||
'https://169.254.169.254/*' => Http::response(['role' => 'internal'], 200),
|
||||
]);
|
||||
|
||||
$token = integrationAdminToken();
|
||||
$create = $this->withHeader('Authorization', 'Bearer '.$token)
|
||||
->postJson('/api/v1/admin/integration-sites', [
|
||||
'code' => 'redirect-probe-site',
|
||||
'name' => 'Redirect Probe',
|
||||
'wallet_api_url' => 'https://wallet.redirect.test',
|
||||
'admin_account' => [
|
||||
'username' => 'redirect_probe_admin',
|
||||
'nickname' => 'Redirect Probe Admin',
|
||||
'password' => 'secret-strong',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->withHeader('Authorization', 'Bearer '.$token)
|
||||
->postJson('/api/v1/admin/integration-sites/'.(int) $create->json('data.id').'/connectivity-test', [
|
||||
'site_player_id' => '10001',
|
||||
'currency_code' => 'NPR',
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.probe.success', false)
|
||||
->assertJsonPath('data.probe.http_status', 302)
|
||||
->assertJsonMissingPath('data.probe.response_preview')
|
||||
->assertJsonMissing(['must-not-be-returned']);
|
||||
|
||||
Http::assertSentCount(1);
|
||||
});
|
||||
|
||||
test('export parameter sheet excludes plaintext secrets', function (): void {
|
||||
@@ -329,7 +405,7 @@ test('site scoped admin only sees bound integration sites', function (): void {
|
||||
});
|
||||
|
||||
test('player list is filtered by admin site binding', function (): void {
|
||||
$this->seed(\Database\Seeders\CurrencySeeder::class);
|
||||
$this->seed(CurrencySeeder::class);
|
||||
|
||||
Player::query()->create([
|
||||
'site_code' => 'site-a',
|
||||
|
||||
Reference in New Issue
Block a user