mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:50:43 +00:00
refactor(test): dedupe gateway auth e2e lockout setup
This commit is contained in:
@@ -54,6 +54,53 @@ const openTailscaleWs = async (port: number) => {
|
|||||||
|
|
||||||
const originForPort = (port: number) => `http://127.0.0.1:${port}`;
|
const originForPort = (port: number) => `http://127.0.0.1:${port}`;
|
||||||
|
|
||||||
|
function restoreGatewayToken(prevToken: string | undefined) {
|
||||||
|
if (prevToken === undefined) {
|
||||||
|
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||||
|
} else {
|
||||||
|
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startRateLimitedTokenServerWithPairedDeviceToken() {
|
||||||
|
const { loadOrCreateDeviceIdentity } = await import("../infra/device-identity.js");
|
||||||
|
const { approveDevicePairing, getPairedDevice, listDevicePairing } =
|
||||||
|
await import("../infra/device-pairing.js");
|
||||||
|
|
||||||
|
testState.gatewayAuth = {
|
||||||
|
mode: "token",
|
||||||
|
token: "secret",
|
||||||
|
rateLimit: { maxAttempts: 1, windowMs: 60_000, lockoutMs: 60_000, exemptLoopback: false },
|
||||||
|
// oxlint-disable-next-line typescript/no-explicit-any
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const { server, ws, port, prevToken } = await startServerWithClient();
|
||||||
|
try {
|
||||||
|
const initial = await connectReq(ws, { token: "secret" });
|
||||||
|
if (!initial.ok) {
|
||||||
|
const list = await listDevicePairing();
|
||||||
|
const pending = list.pending.at(0);
|
||||||
|
expect(pending?.requestId).toBeDefined();
|
||||||
|
if (pending?.requestId) {
|
||||||
|
await approveDevicePairing(pending.requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const identity = loadOrCreateDeviceIdentity();
|
||||||
|
const paired = await getPairedDevice(identity.deviceId);
|
||||||
|
const deviceToken = paired?.tokens?.operator?.token;
|
||||||
|
expect(deviceToken).toBeDefined();
|
||||||
|
|
||||||
|
ws.close();
|
||||||
|
return { server, port, prevToken, deviceToken: String(deviceToken ?? "") };
|
||||||
|
} catch (err) {
|
||||||
|
ws.close();
|
||||||
|
await server.close();
|
||||||
|
restoreGatewayToken(prevToken);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe("gateway server auth/connect", () => {
|
describe("gateway server auth/connect", () => {
|
||||||
describe("default auth (token)", () => {
|
describe("default auth (token)", () => {
|
||||||
let server: Awaited<ReturnType<typeof startGatewayServer>>;
|
let server: Awaited<ReturnType<typeof startGatewayServer>>;
|
||||||
@@ -693,36 +740,9 @@ describe("gateway server auth/connect", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("keeps shared-secret lockout separate from device-token auth", async () => {
|
test("keeps shared-secret lockout separate from device-token auth", async () => {
|
||||||
const { loadOrCreateDeviceIdentity } = await import("../infra/device-identity.js");
|
const { server, port, prevToken, deviceToken } =
|
||||||
const { approveDevicePairing, getPairedDevice, listDevicePairing } =
|
await startRateLimitedTokenServerWithPairedDeviceToken();
|
||||||
await import("../infra/device-pairing.js");
|
|
||||||
testState.gatewayAuth = {
|
|
||||||
mode: "token",
|
|
||||||
token: "secret",
|
|
||||||
rateLimit: { maxAttempts: 1, windowMs: 60_000, lockoutMs: 60_000, exemptLoopback: false },
|
|
||||||
// oxlint-disable-next-line typescript/no-explicit-any
|
|
||||||
} as any;
|
|
||||||
const prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = "secret";
|
|
||||||
const port = await getFreePort();
|
|
||||||
const server = await startGatewayServer(port);
|
|
||||||
try {
|
try {
|
||||||
const ws = await openWs(port);
|
|
||||||
const initial = await connectReq(ws, { token: "secret" });
|
|
||||||
if (!initial.ok) {
|
|
||||||
const list = await listDevicePairing();
|
|
||||||
const pending = list.pending.at(0);
|
|
||||||
expect(pending?.requestId).toBeDefined();
|
|
||||||
if (pending?.requestId) {
|
|
||||||
await approveDevicePairing(pending.requestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const identity = loadOrCreateDeviceIdentity();
|
|
||||||
const paired = await getPairedDevice(identity.deviceId);
|
|
||||||
const deviceToken = paired?.tokens?.operator?.token;
|
|
||||||
expect(deviceToken).toBeDefined();
|
|
||||||
ws.close();
|
|
||||||
|
|
||||||
const wsBadShared = await openWs(port);
|
const wsBadShared = await openWs(port);
|
||||||
const badShared = await connectReq(wsBadShared, { token: "wrong", device: null });
|
const badShared = await connectReq(wsBadShared, { token: "wrong", device: null });
|
||||||
expect(badShared.ok).toBe(false);
|
expect(badShared.ok).toBe(false);
|
||||||
@@ -740,45 +760,14 @@ describe("gateway server auth/connect", () => {
|
|||||||
wsDevice.close();
|
wsDevice.close();
|
||||||
} finally {
|
} finally {
|
||||||
await server.close();
|
await server.close();
|
||||||
if (prevToken === undefined) {
|
restoreGatewayToken(prevToken);
|
||||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
||||||
} else {
|
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("keeps device-token lockout separate from shared-secret auth", async () => {
|
test("keeps device-token lockout separate from shared-secret auth", async () => {
|
||||||
const { loadOrCreateDeviceIdentity } = await import("../infra/device-identity.js");
|
const { server, port, prevToken, deviceToken } =
|
||||||
const { approveDevicePairing, getPairedDevice, listDevicePairing } =
|
await startRateLimitedTokenServerWithPairedDeviceToken();
|
||||||
await import("../infra/device-pairing.js");
|
|
||||||
testState.gatewayAuth = {
|
|
||||||
mode: "token",
|
|
||||||
token: "secret",
|
|
||||||
rateLimit: { maxAttempts: 1, windowMs: 60_000, lockoutMs: 60_000, exemptLoopback: false },
|
|
||||||
// oxlint-disable-next-line typescript/no-explicit-any
|
|
||||||
} as any;
|
|
||||||
const prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = "secret";
|
|
||||||
const port = await getFreePort();
|
|
||||||
const server = await startGatewayServer(port);
|
|
||||||
try {
|
try {
|
||||||
const ws = await openWs(port);
|
|
||||||
const initial = await connectReq(ws, { token: "secret" });
|
|
||||||
if (!initial.ok) {
|
|
||||||
const list = await listDevicePairing();
|
|
||||||
const pending = list.pending.at(0);
|
|
||||||
expect(pending?.requestId).toBeDefined();
|
|
||||||
if (pending?.requestId) {
|
|
||||||
await approveDevicePairing(pending.requestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const identity = loadOrCreateDeviceIdentity();
|
|
||||||
const paired = await getPairedDevice(identity.deviceId);
|
|
||||||
const deviceToken = paired?.tokens?.operator?.token;
|
|
||||||
expect(deviceToken).toBeDefined();
|
|
||||||
ws.close();
|
|
||||||
|
|
||||||
const wsBadDevice = await openWs(port);
|
const wsBadDevice = await openWs(port);
|
||||||
const badDevice = await connectReq(wsBadDevice, { token: "wrong" });
|
const badDevice = await connectReq(wsBadDevice, { token: "wrong" });
|
||||||
expect(badDevice.ok).toBe(false);
|
expect(badDevice.ok).toBe(false);
|
||||||
@@ -802,11 +791,7 @@ describe("gateway server auth/connect", () => {
|
|||||||
wsDeviceReal.close();
|
wsDeviceReal.close();
|
||||||
} finally {
|
} finally {
|
||||||
await server.close();
|
await server.close();
|
||||||
if (prevToken === undefined) {
|
restoreGatewayToken(prevToken);
|
||||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
||||||
} else {
|
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user