diff --git a/src/channels/plugins/directory-config-helpers.test.ts b/src/channels/plugins/directory-config-helpers.test.ts index c9ba1429791..15aa8f0d298 100644 --- a/src/channels/plugins/directory-config-helpers.test.ts +++ b/src/channels/plugins/directory-config-helpers.test.ts @@ -6,6 +6,13 @@ import { listDirectoryUserEntriesFromAllowFrom, } from "./directory-config-helpers.js"; +function expectUserDirectoryEntries(entries: unknown) { + expect(entries).toEqual([ + { kind: "user", id: "alice" }, + { kind: "user", id: "carla" }, + ]); +} + describe("listDirectoryUserEntriesFromAllowFrom", () => { it("normalizes, deduplicates, filters, and limits user ids", () => { const entries = listDirectoryUserEntriesFromAllowFrom({ @@ -15,10 +22,7 @@ describe("listDirectoryUserEntriesFromAllowFrom", () => { limit: 2, }); - expect(entries).toEqual([ - { kind: "user", id: "alice" }, - { kind: "user", id: "carla" }, - ]); + expectUserDirectoryEntries(entries); }); }); @@ -54,10 +58,7 @@ describe("listDirectoryUserEntriesFromAllowFromAndMapKeys", () => { limit: 2, }); - expect(entries).toEqual([ - { kind: "user", id: "alice" }, - { kind: "user", id: "carla" }, - ]); + expectUserDirectoryEntries(entries); }); }); diff --git a/src/channels/plugins/directory-config-helpers.ts b/src/channels/plugins/directory-config-helpers.ts index 72f589bc0a7..edfab553677 100644 --- a/src/channels/plugins/directory-config-helpers.ts +++ b/src/channels/plugins/directory-config-helpers.ts @@ -22,12 +22,12 @@ export function toDirectoryEntries(kind: "user" | "group", ids: string[]): Chann return ids.map((id) => ({ kind, id }) as const); } -function collectDirectoryIdsFromEntries(params: { - entries?: readonly unknown[]; +function normalizeDirectoryIds(params: { + rawIds: readonly string[]; normalizeId?: (entry: string) => string | null | undefined; }): string[] { - return (params.entries ?? []) - .map((entry) => String(entry).trim()) + return params.rawIds + .map((entry) => entry.trim()) .filter((entry) => Boolean(entry) && entry !== "*") .map((entry) => { const normalized = params.normalizeId ? params.normalizeId(entry) : entry; @@ -36,18 +36,24 @@ function collectDirectoryIdsFromEntries(params: { .filter(Boolean); } +function collectDirectoryIdsFromEntries(params: { + entries?: readonly unknown[]; + normalizeId?: (entry: string) => string | null | undefined; +}): string[] { + return normalizeDirectoryIds({ + rawIds: (params.entries ?? []).map((entry) => String(entry)), + normalizeId: params.normalizeId, + }); +} + function collectDirectoryIdsFromMapKeys(params: { groups?: Record; normalizeId?: (entry: string) => string | null | undefined; }): string[] { - return Object.keys(params.groups ?? {}) - .map((entry) => entry.trim()) - .filter((entry) => Boolean(entry) && entry !== "*") - .map((entry) => { - const normalized = params.normalizeId ? params.normalizeId(entry) : entry; - return typeof normalized === "string" ? normalized.trim() : ""; - }) - .filter(Boolean); + return normalizeDirectoryIds({ + rawIds: Object.keys(params.groups ?? {}), + normalizeId: params.normalizeId, + }); } function dedupeDirectoryIds(ids: string[]): string[] { diff --git a/src/channels/plugins/plugins-core.test.ts b/src/channels/plugins/plugins-core.test.ts index 9ccbaac8946..30ed835873d 100644 --- a/src/channels/plugins/plugins-core.test.ts +++ b/src/channels/plugins/plugins-core.test.ts @@ -410,33 +410,43 @@ describe("resolveChannelConfigWrites", () => { }); describe("authorizeConfigWrite", () => { - it("blocks when a target account disables writes", () => { - const cfg = makeSlackConfigWritesCfg("work"); + function expectConfigWriteBlocked(params: { + disabledAccountId: string; + reason: "target-disabled" | "origin-disabled"; + blockedScope: "target" | "origin"; + }) { expect( authorizeConfigWrite({ - cfg, + cfg: makeSlackConfigWritesCfg(params.disabledAccountId), origin: { channelId: "slack", accountId: "default" }, target: resolveExplicitConfigWriteTarget({ channelId: "slack", accountId: "work" }), }), ).toEqual({ allowed: false, + reason: params.reason, + blockedScope: { + kind: params.blockedScope, + scope: { + channelId: "slack", + accountId: params.blockedScope === "target" ? "work" : "default", + }, + }, + }); + } + + it("blocks when a target account disables writes", () => { + expectConfigWriteBlocked({ + disabledAccountId: "work", reason: "target-disabled", - blockedScope: { kind: "target", scope: { channelId: "slack", accountId: "work" } }, + blockedScope: "target", }); }); it("blocks when the origin account disables writes", () => { - const cfg = makeSlackConfigWritesCfg("default"); - expect( - authorizeConfigWrite({ - cfg, - origin: { channelId: "slack", accountId: "default" }, - target: resolveExplicitConfigWriteTarget({ channelId: "slack", accountId: "work" }), - }), - ).toEqual({ - allowed: false, + expectConfigWriteBlocked({ + disabledAccountId: "default", reason: "origin-disabled", - blockedScope: { kind: "origin", scope: { channelId: "slack", accountId: "default" } }, + blockedScope: "origin", }); }); diff --git a/src/channels/plugins/status.ts b/src/channels/plugins/status.ts index cc7de671a3a..689c50c6710 100644 --- a/src/channels/plugins/status.ts +++ b/src/channels/plugins/status.ts @@ -41,6 +41,19 @@ async function buildSnapshotFromAccount(params: { }; } +function inspectChannelAccount(params: { + plugin: ChannelPlugin; + cfg: OpenClawConfig; + accountId: string; +}): ResolvedAccount | null { + return (params.plugin.config.inspectAccount?.(params.cfg, params.accountId) ?? + inspectReadOnlyChannelAccount({ + channelId: params.plugin.id, + cfg: params.cfg, + accountId: params.accountId, + })) as ResolvedAccount | null; +} + export async function buildReadOnlySourceChannelAccountSnapshot(params: { plugin: ChannelPlugin; cfg: OpenClawConfig; @@ -49,13 +62,7 @@ export async function buildReadOnlySourceChannelAccountSnapshot probe?: unknown; audit?: unknown; }): Promise { - const inspectedAccount = - params.plugin.config.inspectAccount?.(params.cfg, params.accountId) ?? - inspectReadOnlyChannelAccount({ - channelId: params.plugin.id, - cfg: params.cfg, - accountId: params.accountId, - }); + const inspectedAccount = inspectChannelAccount(params); if (!inspectedAccount) { return null; } @@ -73,15 +80,9 @@ export async function buildChannelAccountSnapshot(params: { probe?: unknown; audit?: unknown; }): Promise { - const inspectedAccount = - params.plugin.config.inspectAccount?.(params.cfg, params.accountId) ?? - inspectReadOnlyChannelAccount({ - channelId: params.plugin.id, - cfg: params.cfg, - accountId: params.accountId, - }); - const account = (inspectedAccount ?? - params.plugin.config.resolveAccount(params.cfg, params.accountId)) as ResolvedAccount; + const inspectedAccount = inspectChannelAccount(params); + const account = + inspectedAccount ?? params.plugin.config.resolveAccount(params.cfg, params.accountId); return await buildSnapshotFromAccount({ ...params, account,