fix для работы с vmangos
This commit is contained in:
@@ -5,6 +5,7 @@ namespace Tests\Feature;
|
||||
use App\Models\GameAccount;
|
||||
use App\Services\VMangosSrpService;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
@@ -39,6 +40,8 @@ class LauncherGameTicketFeatureTest extends TestCase
|
||||
|
||||
public function test_game_ticket_is_issued_and_replaces_previous_generation(): void
|
||||
{
|
||||
config(['moonwell.timezone' => 'Europe/Samara']);
|
||||
$this->travelTo(Carbon::parse('2026-10-04 12:00:00', 'UTC'));
|
||||
$account = new GameAccount;
|
||||
$account->id = 7;
|
||||
$account->username = 'PLAYERONE';
|
||||
@@ -49,6 +52,9 @@ class LauncherGameTicketFeatureTest extends TestCase
|
||||
$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->assertSame('2026-10-04 12:00:00.000000', $row->issued_at);
|
||||
$this->assertSame('2026-10-04 12:01:00.000000', $row->expires_at);
|
||||
$this->assertSame('2026-10-04T12:01:00Z', $response->json('expires_at'));
|
||||
$this->assertTrue(app(VMangosSrpService::class)->credentialsMatch('PLAYERONE', $first, strtoupper(bin2hex($row->srp_salt)), strtoupper(bin2hex($row->srp_verifier))));
|
||||
$generation = $row->generation_id;
|
||||
|
||||
|
||||
@@ -2,16 +2,23 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Exceptions\DuplicateGameAccountException;
|
||||
use App\Models\User;
|
||||
use App\Services\AzerothCoreSrpService;
|
||||
use App\Services\VMangosAccountRegistrar;
|
||||
use App\Services\VMangosAccountService;
|
||||
use App\Services\VMangosSrpService;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
class VMangosAccountIntegrationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@@ -58,10 +65,14 @@ class VMangosAccountIntegrationTest extends TestCase
|
||||
Schema::connection('vmangos_auth')->create('site_credentials', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('account_id')->primary();
|
||||
$table->unsignedInteger('legacy_account_id')->nullable()->unique();
|
||||
$table->char('salt_hex', 64);
|
||||
$table->char('verifier_hex', 64);
|
||||
$table->unsignedBigInteger('web_user_id')->nullable()->unique();
|
||||
$table->char('salt_hex', 64)->nullable();
|
||||
$table->char('verifier_hex', 64)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
Schema::connection('vmangos_auth')->create('launcher_ticket', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('account_id')->primary();
|
||||
});
|
||||
Schema::connection('vmangos_characters')->create('characters', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('guid')->primary();
|
||||
$table->unsignedInteger('account');
|
||||
@@ -93,6 +104,9 @@ class VMangosAccountIntegrationTest extends TestCase
|
||||
$registered = app(VMangosAccountRegistrar::class)->register('PlayerOne', 'player@example.com', 'Pass1234');
|
||||
$id = $registered['id'];
|
||||
$this->assertSame('PLAYERONE', $registered['username']);
|
||||
$webUser = User::where('name', 'PLAYERONE')->firstOrFail();
|
||||
$this->assertTrue(Hash::check('Pass1234', $webUser->password));
|
||||
$this->assertDatabaseHas('site_credentials', ['account_id' => $id, 'web_user_id' => $webUser->id, 'salt_hex' => null, 'verifier_hex' => null], 'vmangos_auth');
|
||||
$this->assertDatabaseHas('account', ['id' => $id, 'username' => 'PLAYERONE', 'expansion' => 0], 'vmangos_auth');
|
||||
$this->assertSame(2, DB::connection('vmangos_auth')->table('realmcharacters')->where('acctid', $id)->count());
|
||||
$this->assertDatabaseHas('account_access', ['id' => $id, 'RealmID' => -1, 'gmlevel' => 0], 'vmangos_auth');
|
||||
@@ -104,6 +118,8 @@ class VMangosAccountIntegrationTest extends TestCase
|
||||
$this->assertNotNull($user);
|
||||
$this->assertTrue($accounts->validatePassword($user, 'Pass1234'));
|
||||
$this->assertFalse($accounts->validatePassword($user, 'Wrong1234'));
|
||||
$this->assertFalse($accounts->validatePassword($user, 'pass1234'));
|
||||
$this->assertSame($webUser->id, $user->webUserId);
|
||||
|
||||
DB::connection('vmangos_auth')->table('account')->where('id', $id)->update(['last_login' => '0000-00-00 00:00:00']);
|
||||
$this->assertSame('—', $accounts->findUserById($id)->lastLoginLabel());
|
||||
@@ -123,6 +139,126 @@ class VMangosAccountIntegrationTest extends TestCase
|
||||
$fresh = $accounts->findUserById($id);
|
||||
$this->assertFalse($accounts->validatePassword($fresh, 'Pass1234'));
|
||||
$this->assertTrue($accounts->validatePassword($fresh, 'NewPass123'));
|
||||
$this->assertTrue(Hash::check('NewPass123', $webUser->fresh()->password));
|
||||
$this->assertSame($gameCredential->v, DB::connection('vmangos_auth')->table('account')->where('id', $id)->value('v'));
|
||||
}
|
||||
|
||||
public function test_import_reads_web_users_and_preserves_web_hashes_and_repeated_game_credentials(): void
|
||||
{
|
||||
DB::connection('vmangos_auth')->table('realmlist')->insert(['id' => 1]);
|
||||
$webUser = User::create(['name' => 'Existing', 'email' => 'existing@example.com', 'password' => 'ExistingWebPassword123']);
|
||||
$hash = $webUser->password;
|
||||
$this->artisan('vmangos:import-web-accounts', ['--dry-run' => true])
|
||||
->expectsOutput('Created: 1; linked: 0; already imported: 0.')->assertSuccessful();
|
||||
$this->assertSame(0, DB::connection('vmangos_auth')->table('account')->count());
|
||||
$this->artisan('vmangos:import-web-accounts')->assertSuccessful();
|
||||
$accounts = app(VMangosAccountService::class);
|
||||
$user = $accounts->findUserByUsername('existing');
|
||||
$this->assertSame($webUser->id, $user->webUserId);
|
||||
$this->assertSame($hash, $webUser->fresh()->password);
|
||||
$this->assertTrue($accounts->validatePassword($user, 'ExistingWebPassword123'));
|
||||
$game = DB::connection('vmangos_auth')->table('account')->where('id', $user->id)->first();
|
||||
$this->assertFalse(app(VMangosSrpService::class)->credentialsMatch('EXISTING', 'ExistingWebPassword123', $game->s, $game->v));
|
||||
$this->assertDatabaseHas('account_access', ['id' => $user->id, 'RealmID' => -1, 'gmlevel' => 0], 'vmangos_auth');
|
||||
$this->assertDatabaseHas('realmcharacters', ['acctid' => $user->id, 'realmid' => 1], 'vmangos_auth');
|
||||
$this->artisan('vmangos:import-web-accounts')
|
||||
->expectsOutput('Created: 0; linked: 0; already imported: 1.')->assertSuccessful();
|
||||
$this->assertSame($game->v, DB::connection('vmangos_auth')->table('account')->where('id', $user->id)->value('v'));
|
||||
$this->post('/login', ['username' => 'existing', 'password' => 'ExistingWebPassword123'])->assertRedirect();
|
||||
$this->assertAuthenticated();
|
||||
}
|
||||
|
||||
public function test_linking_existing_account_keeps_privileges_and_revokes_sessions(): void
|
||||
{
|
||||
$target = DB::connection('vmangos_auth');
|
||||
$id = $target->table('account')->insertGetId([
|
||||
'username' => 'ADMIN', 'email' => 'admin@example.com', 's' => 'old', 'v' => 'old',
|
||||
'sessionkey' => 'old-session', 'joindate' => '2025-01-01 00:00:00',
|
||||
]);
|
||||
$target->table('account_access')->insert(['id' => $id, 'RealmID' => -1, 'gmlevel' => 3]);
|
||||
$target->table('launcher_ticket')->insert(['account_id' => $id]);
|
||||
[$salt, $verifier] = app(AzerothCoreSrpService::class)->makeRegistrationData('ADMIN', 'OldPass123');
|
||||
$target->table('site_credentials')->insert([
|
||||
'account_id' => $id, 'legacy_account_id' => 8, 'salt_hex' => $salt, 'verifier_hex' => $verifier,
|
||||
]);
|
||||
$webUser = User::create(['name' => 'Admin', 'email' => 'admin@example.com', 'password' => 'WebPass123']);
|
||||
DB::table('oauth_access_tokens')->insert([
|
||||
'id' => 'old-token', 'user_id' => 99, 'client_id' => 'old-client', 'scopes' => '[]', 'revoked' => false,
|
||||
]);
|
||||
DB::table('oauth_refresh_tokens')->insert(['id' => 'old-refresh', 'access_token_id' => 'old-token', 'revoked' => false]);
|
||||
$this->artisan('vmangos:import-web-accounts', ['--dry-run' => true])->assertSuccessful();
|
||||
$this->assertDatabaseHas('oauth_access_tokens', ['id' => 'old-token', 'revoked' => false]);
|
||||
$this->artisan('vmangos:import-web-accounts')
|
||||
->expectsOutput('Created: 0; linked: 1; already imported: 0.')->assertSuccessful();
|
||||
$accounts = app(VMangosAccountService::class);
|
||||
$user = $accounts->findUserByUsername('admin');
|
||||
$this->assertSame($id, $user->id);
|
||||
$this->assertSame($webUser->id, $user->webUserId);
|
||||
$this->assertSame(3, $user->gmLevel);
|
||||
$this->assertTrue($accounts->validatePassword($user, 'WebPass123'));
|
||||
$this->assertFalse($accounts->validatePassword($user, 'OldPass123'));
|
||||
$this->assertDatabaseHas('site_credentials', ['account_id' => $id, 'salt_hex' => null, 'verifier_hex' => null], 'vmangos_auth');
|
||||
$this->assertNull($target->table('account')->where('id', $id)->value('sessionkey'));
|
||||
$this->assertSame(0, $target->table('launcher_ticket')->count());
|
||||
$this->assertDatabaseHas('oauth_access_tokens', ['id' => 'old-token', 'revoked' => true]);
|
||||
$this->assertDatabaseHas('oauth_refresh_tokens', ['id' => 'old-refresh', 'revoked' => true]);
|
||||
}
|
||||
|
||||
public function test_import_conflict_does_not_create_other_users(): void
|
||||
{
|
||||
User::create(['name' => 'FirstUser', 'email' => 'first@example.com', 'password' => 'WebPass123']);
|
||||
User::create(['name' => 'SecondUser', 'email' => 'second@example.com', 'password' => 'WebPass123']);
|
||||
DB::connection('vmangos_auth')->table('account')->insert([
|
||||
'username' => 'SECONDUSER', 'email' => 'someone-else@example.com',
|
||||
's' => 'old', 'v' => 'old', 'joindate' => '2025-01-01 00:00:00',
|
||||
]);
|
||||
$this->artisan('vmangos:import-web-accounts')->assertFailed();
|
||||
$this->assertSame(1, DB::connection('vmangos_auth')->table('account')->count());
|
||||
$this->assertSame(0, DB::connection('vmangos_auth')->table('site_credentials')->count());
|
||||
}
|
||||
|
||||
public function test_import_rejects_duplicate_normalized_web_names(): void
|
||||
{
|
||||
User::create(['name' => 'Duplicate', 'email' => 'first@example.com', 'password' => 'WebPass123']);
|
||||
User::create(['name' => 'duplicate', 'email' => 'second@example.com', 'password' => 'WebPass123']);
|
||||
$this->artisan('vmangos:import-web-accounts')->assertFailed();
|
||||
$this->assertSame(0, DB::connection('vmangos_auth')->table('account')->count());
|
||||
}
|
||||
|
||||
public function test_missing_web_user_cannot_use_legacy_password_fallback(): void
|
||||
{
|
||||
$result = app(VMangosAccountRegistrar::class)->register('PlayerOne', 'player@example.com', 'Pass1234');
|
||||
$accounts = app(VMangosAccountService::class);
|
||||
$user = $accounts->findUserById($result['id']);
|
||||
User::findOrFail($user->webUserId)->delete();
|
||||
$this->assertFalse($accounts->validatePassword($user, 'Pass1234'));
|
||||
}
|
||||
|
||||
public function test_registration_duplicate_web_email_does_not_leave_game_account(): void
|
||||
{
|
||||
User::create(['name' => 'Existing', 'email' => 'player@example.com', 'password' => 'Pass1234']);
|
||||
try {
|
||||
app(VMangosAccountRegistrar::class)->register('PlayerOne', 'player@example.com', 'Pass1234');
|
||||
$this->fail('Expected duplicate email to be rejected.');
|
||||
} catch (DuplicateGameAccountException $exception) {
|
||||
$this->assertSame(0, DB::connection('vmangos_auth')->table('account')->count());
|
||||
$this->assertSame(1, User::count());
|
||||
}
|
||||
}
|
||||
|
||||
public function test_vmangos_dates_use_configured_timezone_in_profile_and_admin(): void
|
||||
{
|
||||
config(['moonwell.timezone' => 'Europe/Samara']);
|
||||
$result = app(VMangosAccountRegistrar::class)->register('PlayerOne', 'player@example.com', 'Pass1234');
|
||||
DB::connection('vmangos_auth')->table('account')->where('id', $result['id'])->update([
|
||||
'joindate' => '2026-10-03 22:30:00', 'last_login' => '2026-10-04 01:15:00',
|
||||
]);
|
||||
$accounts = app(VMangosAccountService::class);
|
||||
$user = $accounts->findUserById($result['id']);
|
||||
$this->assertSame('04.10.2026 02:30', $user->joinedAtLabel());
|
||||
$this->assertSame('04.10.2026 05:15', $user->lastLoginLabel());
|
||||
$row = $accounts->listAccounts()->first();
|
||||
$this->assertSame('04.10.2026 02:30', $row['joined_at']);
|
||||
$this->assertSame('04.10.2026 05:15', $row['last_login_at']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user