vmangos
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameAccount;
|
||||
use App\Services\AzerothCoreSrpService;
|
||||
use App\Services\VMangosSrpService;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
@@ -11,115 +11,63 @@ use Tests\TestCase;
|
||||
|
||||
class LauncherGameTicketFeatureTest extends TestCase
|
||||
{
|
||||
private const string SESSION_TOKEN = 'launcher-session-access-token';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config(['auth.guards.api' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'game_accounts_eloquent',
|
||||
]]);
|
||||
config([
|
||||
'app.key' => 'base64:'.base64_encode(str_repeat('m', 32)),
|
||||
'auth.guards.api' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'game_accounts_eloquent',
|
||||
],
|
||||
'moonwell.auth_connection' => 'azerothcore_auth',
|
||||
'moonwell.launcher.game_ticket_ttl' => 60,
|
||||
'moonwell.launcher.client_build' => 12340,
|
||||
'database.connections.azerothcore_auth' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
'prefix' => '',
|
||||
],
|
||||
'moonwell.auth_connection' => 'vmangos_auth',
|
||||
'database.connections.vmangos_auth' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => ''],
|
||||
]);
|
||||
DB::purge('azerothcore_auth');
|
||||
|
||||
Schema::connection('azerothcore_auth')->create('launcher_ticket', function (Blueprint $table): void {
|
||||
DB::purge('vmangos_auth');
|
||||
Schema::connection('vmangos_auth')->create('launcher_ticket', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('account_id')->primary();
|
||||
$table->binary('generation_id')->unique();
|
||||
$table->binary('generation_id');
|
||||
$table->binary('srp_salt');
|
||||
$table->binary('srp_verifier');
|
||||
$table->unsignedSmallInteger('client_build');
|
||||
$table->binary('launcher_session_hash')->nullable();
|
||||
$table->timestamp('issued_at');
|
||||
$table->timestamp('expires_at');
|
||||
$table->binary('launcher_session_hash');
|
||||
$table->dateTime('issued_at');
|
||||
$table->dateTime('expires_at');
|
||||
$table->binary('claim_nonce')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function test_authenticated_launcher_receives_ticket_and_database_only_stores_srp_data(): void
|
||||
{
|
||||
$account = $this->authenticate(7, 'PlayerOne');
|
||||
|
||||
$response = $this->postJson('/api/launcher/game-ticket', ['client_build' => 12340], [
|
||||
'Authorization' => 'Bearer '.self::SESSION_TOKEN,
|
||||
])->assertOk()->assertHeader('Cache-Control', 'no-store, private');
|
||||
$ticket = $response->json('ticket');
|
||||
|
||||
$response->assertJsonPath('account', 'PLAYERONE');
|
||||
$this->assertMatchesRegularExpression('/^[A-Z0-9]{16}$/', $ticket);
|
||||
$this->assertNotNull($response->json('expires_at'));
|
||||
|
||||
$row = DB::connection('azerothcore_auth')->table('launcher_ticket')->where('account_id', $account->id)->first();
|
||||
$this->assertNotNull($row);
|
||||
$this->assertSame(16, strlen($row->generation_id));
|
||||
$this->assertSame(32, strlen($row->srp_salt));
|
||||
$this->assertSame(32, strlen($row->srp_verifier));
|
||||
$this->assertSame(12340, $row->client_build);
|
||||
$this->assertSame(hash('sha256', self::SESSION_TOKEN, true), $row->launcher_session_hash);
|
||||
$this->assertTrue(app(AzerothCoreSrpService::class)->credentialsMatch(
|
||||
'PLAYERONE',
|
||||
$ticket,
|
||||
bin2hex($row->srp_salt),
|
||||
bin2hex($row->srp_verifier),
|
||||
));
|
||||
$this->assertStringNotContainsString($ticket, serialize($row));
|
||||
}
|
||||
|
||||
public function test_new_generation_revokes_previous_ticket(): void
|
||||
{
|
||||
$account = $this->authenticate(9, 'DevPlayer');
|
||||
$headers = ['Authorization' => 'Bearer '.self::SESSION_TOKEN];
|
||||
|
||||
$firstTicket = $this->postJson('/api/launcher/game-ticket', ['client_build' => 12340], $headers)
|
||||
->assertOk()->json('ticket');
|
||||
$firstGeneration = DB::connection('azerothcore_auth')->table('launcher_ticket')
|
||||
->where('account_id', $account->id)->value('generation_id');
|
||||
|
||||
$secondTicket = $this->postJson('/api/launcher/game-ticket', ['client_build' => 12340], $headers)
|
||||
->assertOk()->json('ticket');
|
||||
$row = DB::connection('azerothcore_auth')->table('launcher_ticket')
|
||||
->where('account_id', $account->id)->first();
|
||||
$srp = app(AzerothCoreSrpService::class);
|
||||
|
||||
$this->assertNotSame($firstTicket, $secondTicket);
|
||||
$this->assertNotSame($firstGeneration, $row->generation_id);
|
||||
$this->assertFalse($srp->credentialsMatch('DEVPLAYER', $firstTicket, bin2hex($row->srp_salt), bin2hex($row->srp_verifier)));
|
||||
$this->assertTrue($srp->credentialsMatch('DEVPLAYER', $secondTicket, bin2hex($row->srp_salt), bin2hex($row->srp_verifier)));
|
||||
}
|
||||
|
||||
public function test_ticket_endpoint_requires_launcher_authentication(): void
|
||||
{
|
||||
$this->postJson('/api/launcher/game-ticket', ['client_build' => 12340])->assertUnauthorized();
|
||||
}
|
||||
|
||||
public function test_ticket_endpoint_rejects_wrong_client_build(): void
|
||||
{
|
||||
$this->authenticate(10, 'PlayerTwo');
|
||||
|
||||
$this->postJson('/api/launcher/game-ticket', ['client_build' => 99999], [
|
||||
'Authorization' => 'Bearer '.self::SESSION_TOKEN,
|
||||
])->assertUnprocessable();
|
||||
|
||||
$this->assertSame(0, DB::connection('azerothcore_auth')->table('launcher_ticket')->count());
|
||||
}
|
||||
|
||||
private function authenticate(int $id, string $username): GameAccount
|
||||
public function test_game_ticket_is_issued_and_replaces_previous_generation(): void
|
||||
{
|
||||
$account = new GameAccount;
|
||||
$account->id = $id;
|
||||
$account->username = $username;
|
||||
$account->id = 7;
|
||||
$account->username = 'PLAYERONE';
|
||||
$this->actingAs($account, 'api');
|
||||
|
||||
return $account;
|
||||
$response = $this->withToken('launcher-token')->postJson('/api/launcher/game-ticket', ['client_build' => 5875])->assertOk();
|
||||
$first = $response->json('ticket');
|
||||
$this->assertMatchesRegularExpression('/^[A-Z0-9]{16}$/', $first);
|
||||
$this->assertSame('PLAYERONE', $response->json('account'));
|
||||
$row = DB::connection('vmangos_auth')->table('launcher_ticket')->where('account_id', 7)->first();
|
||||
$this->assertTrue(app(VMangosSrpService::class)->credentialsMatch('PLAYERONE', $first, strtoupper(bin2hex($row->srp_salt)), strtoupper(bin2hex($row->srp_verifier))));
|
||||
$generation = $row->generation_id;
|
||||
|
||||
$this->withToken('launcher-token')->postJson('/api/launcher/game-ticket', ['client_build' => 5875])->assertOk();
|
||||
$this->assertSame(1, DB::connection('vmangos_auth')->table('launcher_ticket')->count());
|
||||
$this->assertNotSame($generation, DB::connection('vmangos_auth')->table('launcher_ticket')->value('generation_id'));
|
||||
}
|
||||
|
||||
public function test_ticket_rejects_wrong_build(): void
|
||||
{
|
||||
$account = new GameAccount;
|
||||
$account->id = 7;
|
||||
$account->username = 'PLAYERONE';
|
||||
$this->actingAs($account, 'api');
|
||||
$this->withToken('launcher-token')->postJson('/api/launcher/game-ticket', ['client_build' => 12340])->assertUnprocessable();
|
||||
}
|
||||
|
||||
public function test_game_ticket_still_requires_launcher_authentication(): void
|
||||
{
|
||||
$this->postJson('/api/launcher/game-ticket', ['client_build' => 5875])->assertUnauthorized();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user