From f197ca503a48a0f3146b8a37f4fb8a930ad6e9a2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 22:18:50 +0100 Subject: [PATCH] test: share gateway pairing authz setup --- .../server.device-pair-approve-authz.test.ts | 113 ++++++++---------- 1 file changed, 50 insertions(+), 63 deletions(-) diff --git a/src/gateway/server.device-pair-approve-authz.test.ts b/src/gateway/server.device-pair-approve-authz.test.ts index 7edb72fb8ad..a4560310917 100644 --- a/src/gateway/server.device-pair-approve-authz.test.ts +++ b/src/gateway/server.device-pair-approve-authz.test.ts @@ -20,36 +20,57 @@ import { installGatewayTestHooks({ scope: "suite" }); +async function issuePairingOnlyOperator(name: string) { + return await issueOperatorToken({ + name, + approvedScopes: ["operator.admin"], + tokenScopes: ["operator.pairing"], + clientId: GATEWAY_CLIENT_NAMES.TEST, + clientMode: GATEWAY_CLIENT_MODES.TEST, + }); +} + +async function requestOperatorDevicePairing(params: { + identity: ReturnType; + scopes: string[]; +}) { + return await requestDevicePairing({ + deviceId: params.identity.identity.deviceId, + publicKey: params.identity.publicKey, + role: "operator", + scopes: params.scopes, + clientId: GATEWAY_CLIENT_NAMES.TEST, + clientMode: GATEWAY_CLIENT_MODES.TEST, + }); +} + +async function openPairingSession( + port: number, + operator: Awaited>, +): Promise { + const pairingWs = await openTrackedWs(port); + await connectOk(pairingWs, { + skipDefaultAuth: true, + deviceToken: operator.token, + deviceIdentityPath: operator.identityPath, + scopes: ["operator.pairing"], + }); + return pairingWs; +} + describe("gateway device.pair.approve caller scope guard", () => { test("rejects approving device scopes above the caller session scopes", async () => { const started = await startServerWithClient("secret"); - const approver = await issueOperatorToken({ - name: "approve-attacker", - approvedScopes: ["operator.admin"], - tokenScopes: ["operator.pairing"], - clientId: GATEWAY_CLIENT_NAMES.TEST, - clientMode: GATEWAY_CLIENT_MODES.TEST, - }); + const approver = await issuePairingOnlyOperator("approve-attacker"); const approverIdentity = loadDeviceIdentity("approve-attacker"); let pairingWs: WebSocket | undefined; try { - const request = await requestDevicePairing({ - deviceId: approverIdentity.identity.deviceId, - publicKey: approverIdentity.publicKey, - role: "operator", + const request = await requestOperatorDevicePairing({ + identity: approverIdentity, scopes: ["operator.admin"], - clientId: GATEWAY_CLIENT_NAMES.TEST, - clientMode: GATEWAY_CLIENT_MODES.TEST, - }); - - pairingWs = await openTrackedWs(started.port); - await connectOk(pairingWs, { - skipDefaultAuth: true, - deviceToken: approver.token, - deviceIdentityPath: approver.identityPath, - scopes: ["operator.pairing"], }); + pairingWs = await openPairingSession(started.port, approver); const approve = await rpcReq(pairingWs, "device.pair.approve", { requestId: request.request.requestId, @@ -70,33 +91,16 @@ describe("gateway device.pair.approve caller scope guard", () => { test("rejects approving another device from a non-admin paired-device session", async () => { const started = await startServerWithClient("secret"); - const approver = await issueOperatorToken({ - name: "approve-cross-device-attacker", - approvedScopes: ["operator.admin"], - tokenScopes: ["operator.pairing"], - clientId: GATEWAY_CLIENT_NAMES.TEST, - clientMode: GATEWAY_CLIENT_MODES.TEST, - }); + const approver = await issuePairingOnlyOperator("approve-cross-device-attacker"); const pending = loadDeviceIdentity("approve-cross-device-target"); let pairingWs: WebSocket | undefined; try { - const request = await requestDevicePairing({ - deviceId: pending.identity.deviceId, - publicKey: pending.publicKey, - role: "operator", - scopes: ["operator.pairing"], - clientId: GATEWAY_CLIENT_NAMES.TEST, - clientMode: GATEWAY_CLIENT_MODES.TEST, - }); - - pairingWs = await openTrackedWs(started.port); - await connectOk(pairingWs, { - skipDefaultAuth: true, - deviceToken: approver.token, - deviceIdentityPath: approver.identityPath, + const request = await requestOperatorDevicePairing({ + identity: pending, scopes: ["operator.pairing"], }); + pairingWs = await openPairingSession(started.port, approver); const approve = await rpcReq(pairingWs, "device.pair.approve", { requestId: request.request.requestId, @@ -116,33 +120,16 @@ describe("gateway device.pair.approve caller scope guard", () => { test("rejects rejecting another device from a non-admin paired-device session", async () => { const started = await startServerWithClient("secret"); - const attacker = await issueOperatorToken({ - name: "reject-cross-device-attacker", - approvedScopes: ["operator.admin"], - tokenScopes: ["operator.pairing"], - clientId: GATEWAY_CLIENT_NAMES.TEST, - clientMode: GATEWAY_CLIENT_MODES.TEST, - }); + const attacker = await issuePairingOnlyOperator("reject-cross-device-attacker"); const pending = loadDeviceIdentity("reject-cross-device-target"); let pairingWs: WebSocket | undefined; try { - const request = await requestDevicePairing({ - deviceId: pending.identity.deviceId, - publicKey: pending.publicKey, - role: "operator", - scopes: ["operator.pairing"], - clientId: GATEWAY_CLIENT_NAMES.TEST, - clientMode: GATEWAY_CLIENT_MODES.TEST, - }); - - pairingWs = await openTrackedWs(started.port); - await connectOk(pairingWs, { - skipDefaultAuth: true, - deviceToken: attacker.token, - deviceIdentityPath: attacker.identityPath, + const request = await requestOperatorDevicePairing({ + identity: pending, scopes: ["operator.pairing"], }); + pairingWs = await openPairingSession(started.port, attacker); const reject = await rpcReq(pairingWs, "device.pair.reject", { requestId: request.request.requestId,