fix(pairing): treat operator.admin as satisfying operator.write

This commit is contained in:
vignesh07
2026-02-21 17:55:22 -08:00
committed by Vignesh
parent a37e12eabc
commit 426d97797d
3 changed files with 15 additions and 5 deletions

View File

@@ -168,7 +168,7 @@ describe("device pairing tokens", () => {
expect(mismatch.reason).toBe("token-mismatch");
});
test("accepts operator.read requests with an operator.admin token scope", async () => {
test("accepts operator.read/operator.write requests with an operator.admin token scope", async () => {
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
const paired = await getPairedDevice("device-1", baseDir);
@@ -183,14 +183,14 @@ describe("device pairing tokens", () => {
});
expect(readOk.ok).toBe(true);
const writeMismatch = await verifyDeviceToken({
const writeOk = await verifyDeviceToken({
deviceId: "device-1",
token,
role: "operator",
scopes: ["operator.write"],
baseDir,
});
expect(writeMismatch).toEqual({ ok: false, reason: "scope-mismatch" });
expect(writeOk.ok).toBe(true);
});
test("treats multibyte same-length token input as mismatch without throwing", async () => {

View File

@@ -26,14 +26,21 @@ describe("roleScopesAllow", () => {
).toBe(true);
});
it("keeps non-read operator scopes explicit", () => {
it("treats operator.write as satisfied by write/admin scopes", () => {
expect(
roleScopesAllow({
role: "operator",
requestedScopes: ["operator.write"],
allowedScopes: ["operator.write"],
}),
).toBe(true);
expect(
roleScopesAllow({
role: "operator",
requestedScopes: ["operator.write"],
allowedScopes: ["operator.admin"],
}),
).toBe(false);
).toBe(true);
});
it("uses strict matching for non-operator roles", () => {

View File

@@ -22,6 +22,9 @@ function operatorScopeSatisfied(requestedScope: string, granted: Set<string>): b
granted.has(OPERATOR_ADMIN_SCOPE)
);
}
if (requestedScope === OPERATOR_WRITE_SCOPE) {
return granted.has(OPERATOR_WRITE_SCOPE) || granted.has(OPERATOR_ADMIN_SCOPE);
}
return granted.has(requestedScope);
}