mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-22 15:31:07 +00:00
Hardening: refresh stale device pairing requests and pending metadata (#50695)
* Docs: clarify device pairing supersede behavior * Device pairing: supersede pending requests on auth changes
This commit is contained in:
@@ -287,6 +287,30 @@ describe("devices cli local fallback", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("devices cli list", () => {
|
||||
it("renders pending scopes when present", async () => {
|
||||
callGateway.mockResolvedValueOnce({
|
||||
pending: [
|
||||
{
|
||||
requestId: "req-1",
|
||||
deviceId: "device-1",
|
||||
displayName: "Device One",
|
||||
role: "operator",
|
||||
scopes: ["operator.admin", "operator.read"],
|
||||
ts: 1,
|
||||
},
|
||||
],
|
||||
paired: [],
|
||||
});
|
||||
|
||||
await runDevicesCommand(["list"]);
|
||||
|
||||
const output = runtime.log.mock.calls.map((entry) => String(entry[0] ?? "")).join("\n");
|
||||
expect(output).toContain("Scopes");
|
||||
expect(output).toContain("operator.admin, operator.read");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
callGateway.mockClear();
|
||||
buildGatewayConnectionDetails.mockClear();
|
||||
|
||||
@@ -39,6 +39,8 @@ type PendingDevice = {
|
||||
deviceId: string;
|
||||
displayName?: string;
|
||||
role?: string;
|
||||
roles?: string[];
|
||||
scopes?: string[];
|
||||
remoteIp?: string;
|
||||
isRepair?: boolean;
|
||||
ts?: number;
|
||||
@@ -197,6 +199,30 @@ function formatTokenSummary(tokens: DeviceTokenSummary[] | undefined) {
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
function formatPendingRoles(request: PendingDevice): string {
|
||||
const role = typeof request.role === "string" ? request.role.trim() : "";
|
||||
if (role) {
|
||||
return role;
|
||||
}
|
||||
const roles = Array.isArray(request.roles)
|
||||
? request.roles.map((item) => item.trim()).filter((item) => item.length > 0)
|
||||
: [];
|
||||
if (roles.length === 0) {
|
||||
return "";
|
||||
}
|
||||
return roles.join(", ");
|
||||
}
|
||||
|
||||
function formatPendingScopes(request: PendingDevice): string {
|
||||
const scopes = Array.isArray(request.scopes)
|
||||
? request.scopes.map((item) => item.trim()).filter((item) => item.length > 0)
|
||||
: [];
|
||||
if (scopes.length === 0) {
|
||||
return "";
|
||||
}
|
||||
return scopes.join(", ");
|
||||
}
|
||||
|
||||
function resolveRequiredDeviceRole(
|
||||
opts: DevicesRpcOpts,
|
||||
): { deviceId: string; role: string } | null {
|
||||
@@ -235,6 +261,7 @@ export function registerDevicesCli(program: Command) {
|
||||
{ key: "Request", header: "Request", minWidth: 10 },
|
||||
{ key: "Device", header: "Device", minWidth: 16, flex: true },
|
||||
{ key: "Role", header: "Role", minWidth: 8 },
|
||||
{ key: "Scopes", header: "Scopes", minWidth: 14, flex: true },
|
||||
{ key: "IP", header: "IP", minWidth: 12 },
|
||||
{ key: "Age", header: "Age", minWidth: 8 },
|
||||
{ key: "Flags", header: "Flags", minWidth: 8 },
|
||||
@@ -242,7 +269,8 @@ export function registerDevicesCli(program: Command) {
|
||||
rows: list.pending.map((req) => ({
|
||||
Request: req.requestId,
|
||||
Device: req.displayName || req.deviceId,
|
||||
Role: req.role ?? "",
|
||||
Role: formatPendingRoles(req),
|
||||
Scopes: formatPendingScopes(req),
|
||||
IP: req.remoteIp ?? "",
|
||||
Age: typeof req.ts === "number" ? formatTimeAgo(Date.now() - req.ts) : "",
|
||||
Flags: req.isRepair ? "repair" : "",
|
||||
|
||||
Reference in New Issue
Block a user