mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 13:59:48 +00:00
18ef15d3ae
**Add approved & assigned issue number here:** Resolves #4349 ## Description: 1. **Private-lobby allowlist.** `create_game` accepts an optional `allowedPublicIds`. It's set by whoever creates the lobby (admin-token gated, no client UI), the game server pulls it out of the config so it's never broadcast to clients or written to the game record, and it rejects any joiner whose OF publicId isn't on the list before they take a slot (stickily, so they can't retry on reconnect). Lobbies created without it behave exactly as before. It is off by default Previews: <img width="241" height="140" alt="image" src="https://github.com/user-attachments/assets/30c4e47b-399d-4720-b25b-a04c63668577" /> <img width="982" height="456" alt="image" src="https://github.com/user-attachments/assets/1b5c68b7-9b99-4ccc-b987-e70c8ec25dce" /> <img width="547" height="369" alt="image" src="https://github.com/user-attachments/assets/1623090b-ea2b-4657-9cd8-903fbabca51b" /> I am not able to manually test all of it since it needs to also run the auth API (infra) and actually be connected to disc and whatnot (but still tested the refused flow).. Also, we would need to place some guards and visual error feedback, but since this only would affect casual of players and is more of a improvement to the feature, I will consider it out of scope for now. ## Please complete the following: - [x] I have added screenshots for all UI updates (no UI changes in this PR) - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file (no new user-facing text) - [x] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: zixer._
115 lines
3.0 KiB
TypeScript
115 lines
3.0 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../../src/core/Schemas", async () => {
|
|
const actual = (await vi.importActual("../../src/core/Schemas")) as any;
|
|
return {
|
|
...actual,
|
|
GameStartInfoSchema: {
|
|
safeParse: (data: any) => ({ success: true, data }),
|
|
},
|
|
ServerPrestartMessageSchema: {
|
|
safeParse: (data: any) => ({ success: true, data }),
|
|
},
|
|
ClientMessageSchema: {
|
|
safeParse: (data: any) => ({ success: true, data }),
|
|
},
|
|
};
|
|
});
|
|
|
|
import { GameType } from "../../src/core/game/Game";
|
|
import { Client } from "../../src/server/Client";
|
|
import { GameServer } from "../../src/server/GameServer";
|
|
|
|
function makeMockWs() {
|
|
return {
|
|
on: () => {},
|
|
removeAllListeners: () => {},
|
|
send: vi.fn(),
|
|
close: vi.fn(),
|
|
readyState: 1,
|
|
};
|
|
}
|
|
|
|
function makeClient(
|
|
clientID: string,
|
|
persistentID: string,
|
|
publicId: string | undefined,
|
|
): Client {
|
|
return new Client(
|
|
clientID,
|
|
persistentID,
|
|
null,
|
|
null,
|
|
undefined,
|
|
"127.0.0.1",
|
|
"TestUser",
|
|
null,
|
|
makeMockWs() as any,
|
|
undefined,
|
|
publicId,
|
|
[],
|
|
);
|
|
}
|
|
|
|
describe("GameServer - allowlist (allowedPublicIds)", () => {
|
|
let mockLogger: any;
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
mockLogger = {
|
|
child: vi.fn().mockReturnThis(),
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
};
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
vi.clearAllTimers();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
function makeGame(allowedPublicIds?: string[]) {
|
|
return new GameServer("test-game", mockLogger, Date.now(), {
|
|
gameType: GameType.Private,
|
|
...(allowedPublicIds ? { allowedPublicIds } : {}),
|
|
} as any);
|
|
}
|
|
|
|
it("admits only listed publicIds and rejects others", () => {
|
|
const game = makeGame(["pub-ok"]);
|
|
expect(game.joinClient(makeClient("c1", "p1", "pub-ok"))).toBe("joined");
|
|
expect(game.joinClient(makeClient("c2", "p2", "pub-no"))).toBe(
|
|
"not_allowlisted",
|
|
);
|
|
expect(game.joinClient(makeClient("c3", "p3", undefined))).toBe(
|
|
"not_allowlisted",
|
|
);
|
|
});
|
|
|
|
it("does not restrict joins when no allowlist is set", () => {
|
|
const game = makeGame();
|
|
expect(game.joinClient(makeClient("c1", "p1", "anything"))).toBe("joined");
|
|
});
|
|
|
|
it("treats an empty allowlist as no restriction", () => {
|
|
const game = makeGame([]);
|
|
expect(game.joinClient(makeClient("c1", "p1", "anything"))).toBe("joined");
|
|
});
|
|
|
|
it("lets a previously-rejected player in once the allowlist is cleared", () => {
|
|
const game = makeGame(["pub-ok"]);
|
|
expect(game.joinClient(makeClient("c2", "p2", "pub-no"))).toBe(
|
|
"not_allowlisted",
|
|
);
|
|
game.updateGameConfig({ allowedPublicIds: [] });
|
|
expect(game.joinClient(makeClient("c2", "p2", "pub-no"))).toBe("joined");
|
|
});
|
|
|
|
it("keeps allowedPublicIds on the stored config (read like other settings)", () => {
|
|
const game = makeGame(["pub-ok"]);
|
|
expect((game.gameConfig as any).allowedPublicIds).toEqual(["pub-ok"]);
|
|
});
|
|
});
|