mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:50:43 +00:00
test: merge device token authz cases
This commit is contained in:
@@ -128,7 +128,7 @@ async function issuePairingScopedTokenForAdminApprovedDevice(name: string): Prom
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => {
|
describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => {
|
||||||
test("rejects a device-token caller rotating another device's token", async () => {
|
test("rejects a device-token caller rotating or revoking another device's token", async () => {
|
||||||
const started = await startServer("secret");
|
const started = await startServer("secret");
|
||||||
const deviceA = await issuePairingScopedTokenForAdminApprovedDevice("idor-device-a");
|
const deviceA = await issuePairingScopedTokenForAdminApprovedDevice("idor-device-a");
|
||||||
const deviceB = await issuePairingScopedTokenForAdminApprovedDevice("idor-device-b");
|
const deviceB = await issuePairingScopedTokenForAdminApprovedDevice("idor-device-b");
|
||||||
@@ -151,6 +151,16 @@ describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => {
|
|||||||
|
|
||||||
const pairedB = await getPairedDevice(deviceB.deviceId);
|
const pairedB = await getPairedDevice(deviceB.deviceId);
|
||||||
expect(pairedB?.tokens?.operator?.token).toBe(deviceB.pairingToken);
|
expect(pairedB?.tokens?.operator?.token).toBe(deviceB.pairingToken);
|
||||||
|
|
||||||
|
const revoke = await rpcReq(pairingWs, "device.token.revoke", {
|
||||||
|
deviceId: deviceB.deviceId,
|
||||||
|
role: "operator",
|
||||||
|
});
|
||||||
|
expect(revoke.ok).toBe(false);
|
||||||
|
expect(revoke.error?.message).toBe("device token revocation denied");
|
||||||
|
|
||||||
|
const pairedBAfterRevoke = await getPairedDevice(deviceB.deviceId);
|
||||||
|
expect(pairedBAfterRevoke?.tokens?.operator?.revokedAtMs).toBeUndefined();
|
||||||
} finally {
|
} finally {
|
||||||
pairingWs?.close();
|
pairingWs?.close();
|
||||||
await started.server.close();
|
await started.server.close();
|
||||||
@@ -158,9 +168,9 @@ describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("allows an admin-scoped caller to rotate another device's token", async () => {
|
test("allows an admin-scoped caller to rotate and revoke another device's token", async () => {
|
||||||
const started = await startServerWithClient("secret");
|
const started = await startServerWithClient("secret");
|
||||||
const device = await issuePairingScopedTokenForAdminApprovedDevice("idor-admin-rotate");
|
const device = await issuePairingScopedTokenForAdminApprovedDevice("idor-admin-rotate-revoke");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await connectOk(started.ws);
|
await connectOk(started.ws);
|
||||||
@@ -172,48 +182,6 @@ describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => {
|
|||||||
});
|
});
|
||||||
expect(rotate.ok).toBe(true);
|
expect(rotate.ok).toBe(true);
|
||||||
expect(rotate.payload?.token).toBeTruthy();
|
expect(rotate.payload?.token).toBeTruthy();
|
||||||
} finally {
|
|
||||||
started.ws.close();
|
|
||||||
await started.server.close();
|
|
||||||
started.envSnapshot.restore();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("rejects a device-token caller revoking another device's token", async () => {
|
|
||||||
const started = await startServer("secret");
|
|
||||||
const deviceA = await issuePairingScopedTokenForAdminApprovedDevice("idor-revoke-a");
|
|
||||||
const deviceB = await issuePairingScopedTokenForAdminApprovedDevice("idor-revoke-b");
|
|
||||||
|
|
||||||
let pairingWs: WebSocket | undefined;
|
|
||||||
try {
|
|
||||||
pairingWs = await connectPairingScopedOperator({
|
|
||||||
port: started.port,
|
|
||||||
identityPath: deviceA.identityPath,
|
|
||||||
deviceToken: deviceA.pairingToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
const revoke = await rpcReq(pairingWs, "device.token.revoke", {
|
|
||||||
deviceId: deviceB.deviceId,
|
|
||||||
role: "operator",
|
|
||||||
});
|
|
||||||
expect(revoke.ok).toBe(false);
|
|
||||||
expect(revoke.error?.message).toBe("device token revocation denied");
|
|
||||||
|
|
||||||
const pairedB = await getPairedDevice(deviceB.deviceId);
|
|
||||||
expect(pairedB?.tokens?.operator?.revokedAtMs).toBeUndefined();
|
|
||||||
} finally {
|
|
||||||
pairingWs?.close();
|
|
||||||
await started.server.close();
|
|
||||||
started.envSnapshot.restore();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("allows an admin-scoped caller to revoke another device's token", async () => {
|
|
||||||
const started = await startServerWithClient("secret");
|
|
||||||
const device = await issuePairingScopedTokenForAdminApprovedDevice("idor-admin-revoke");
|
|
||||||
|
|
||||||
try {
|
|
||||||
await connectOk(started.ws);
|
|
||||||
|
|
||||||
const revoke = await rpcReq<{ revokedAtMs?: number }>(started.ws, "device.token.revoke", {
|
const revoke = await rpcReq<{ revokedAtMs?: number }>(started.ws, "device.token.revoke", {
|
||||||
deviceId: device.deviceId,
|
deviceId: device.deviceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user