mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
test: share gateway pairing authz setup
This commit is contained in:
@@ -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<typeof loadDeviceIdentity>;
|
||||
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<ReturnType<typeof issueOperatorToken>>,
|
||||
): Promise<WebSocket> {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user