Skip to content

Commit 60d3363

Browse files
Sid-QinNaylenvOctane0411Takhoffman
authored
fix(auth): grant senderIsOwner for internal channels with operator.admin scope (openclaw#35704)
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check - pnpm test:macmini Co-authored-by: Naylenv <[email protected]> Co-authored-by: Octane0411 <[email protected]> Co-authored-by: Sid-Qin <[email protected]> Co-authored-by: Tak Hoffman <[email protected]>
1 parent aad372e commit 60d3363

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ Docs: https://docs.openclaw.ai
141141
- Agents/failover cooldown classification: stop treating generic `cooling down` text as provider `rate_limit` so healthy models no longer show false global cooldown/rate-limit warnings while explicit `model_cooldown` markers still trigger failover. (#32972) thanks @stakeswky.
142142
- Agents/failover service-unavailable handling: stop treating bare proxy/CDN `service unavailable` errors as provider overload while keeping them retryable via the timeout/failover path, so transient outages no longer show false rate-limit warnings or block fallback. (#36646) thanks @jnMetaCode.
143143
- Agents/current-time UTC anchor: append a machine-readable UTC suffix alongside local `Current time:` lines in shared cron-style prompt contexts so agents can compare UTC-stamped workspace timestamps without doing timezone math. (#32423) thanks @jriff.
144+
- TUI/webchat command-owner scope alignment: treat internal-channel gateway sessions with `operator.admin` as owner-authorized in command auth, restoring cron/gateway/connector tool access for affected TUI/webchat sessions while keeping external channels on identity-based owner checks. (from #35666, #35673, #35704) Thanks @Naylenv, @Octane0411, and @Sid-Qin.
144145

145146
## 2026.3.2
146147

src/auto-reply/command-auth.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { getChannelDock, listChannelDocks } from "../channels/dock.js";
33
import type { ChannelId } from "../channels/plugins/types.js";
44
import { normalizeAnyChannelId } from "../channels/registry.js";
55
import type { OpenClawConfig } from "../config/config.js";
6-
import { INTERNAL_MESSAGE_CHANNEL, normalizeMessageChannel } from "../utils/message-channel.js";
6+
import {
7+
INTERNAL_MESSAGE_CHANNEL,
8+
isInternalMessageChannel,
9+
normalizeMessageChannel,
10+
} from "../utils/message-channel.js";
711
import type { MsgContext } from "./templating.js";
812

913
export type CommandAuthorization = {
@@ -341,7 +345,12 @@ export function resolveCommandAuthorization(params: {
341345
const senderId = matchedSender ?? senderCandidates[0];
342346

343347
const enforceOwner = Boolean(dock?.commands?.enforceOwnerForCommands);
344-
const senderIsOwner = Boolean(matchedSender);
348+
const senderIsOwnerByIdentity = Boolean(matchedSender);
349+
const senderIsOwnerByScope =
350+
isInternalMessageChannel(ctx.Provider) &&
351+
Array.isArray(ctx.GatewayClientScopes) &&
352+
ctx.GatewayClientScopes.includes("operator.admin");
353+
const senderIsOwner = senderIsOwnerByIdentity || senderIsOwnerByScope;
345354
const ownerAllowlistConfigured = ownerAllowAll || explicitOwners.length > 0;
346355
const requireOwner = enforceOwner || ownerAllowlistConfigured;
347356
const isOwnerForCommands = !requireOwner

src/auto-reply/command-control.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,52 @@ describe("resolveCommandAuthorization", () => {
458458
expect(deniedAuth.isAuthorizedSender).toBe(false);
459459
});
460460
});
461+
462+
it("grants senderIsOwner for internal channel with operator.admin scope", () => {
463+
const cfg = {} as OpenClawConfig;
464+
const ctx = {
465+
Provider: "webchat",
466+
Surface: "webchat",
467+
GatewayClientScopes: ["operator.admin"],
468+
} as MsgContext;
469+
const auth = resolveCommandAuthorization({
470+
ctx,
471+
cfg,
472+
commandAuthorized: true,
473+
});
474+
expect(auth.senderIsOwner).toBe(true);
475+
});
476+
477+
it("does not grant senderIsOwner for internal channel without admin scope", () => {
478+
const cfg = {} as OpenClawConfig;
479+
const ctx = {
480+
Provider: "webchat",
481+
Surface: "webchat",
482+
GatewayClientScopes: ["operator.approvals"],
483+
} as MsgContext;
484+
const auth = resolveCommandAuthorization({
485+
ctx,
486+
cfg,
487+
commandAuthorized: true,
488+
});
489+
expect(auth.senderIsOwner).toBe(false);
490+
});
491+
492+
it("does not grant senderIsOwner for external channel even with admin scope", () => {
493+
const cfg = {} as OpenClawConfig;
494+
const ctx = {
495+
Provider: "telegram",
496+
Surface: "telegram",
497+
From: "telegram:12345",
498+
GatewayClientScopes: ["operator.admin"],
499+
} as MsgContext;
500+
const auth = resolveCommandAuthorization({
501+
ctx,
502+
cfg,
503+
commandAuthorized: true,
504+
});
505+
expect(auth.senderIsOwner).toBe(false);
506+
});
461507
});
462508

463509
describe("control command parsing", () => {

0 commit comments

Comments
 (0)