fix: honor account-scoped setup dm policy

This commit is contained in:
Tak Hoffman 2026-04-03 10:30:51 -05:00
parent c6b8109bd8
commit b1026a0b28
No known key found for this signature in database
4 changed files with 183 additions and 10 deletions

View File

@ -1,7 +1,7 @@
import {
addWildcardAllowFrom,
applySetupAccountConfigPatch,
createNestedChannelParsedAllowFromPrompt,
createNestedChannelDmPolicy,
createStandardChannelSetupStatus,
DEFAULT_ACCOUNT_ID,
formatDocsLink,
@ -37,16 +37,49 @@ const promptAllowFrom = createNestedChannelParsedAllowFromPrompt({
}),
});
const googlechatDmPolicy: ChannelSetupDmPolicy = createNestedChannelDmPolicy({
const googlechatDmPolicy: ChannelSetupDmPolicy = {
label: "Google Chat",
channel,
section: "dm",
policyKey: "channels.googlechat.dm.policy",
allowFromKey: "channels.googlechat.dm.allowFrom",
getCurrent: (cfg) => cfg.channels?.googlechat?.dm?.policy ?? "pairing",
resolveConfigKeys: (_cfg, accountId) =>
accountId && accountId !== DEFAULT_ACCOUNT_ID
? {
policyKey: `channels.googlechat.accounts.${accountId}.dm.policy`,
allowFromKey: `channels.googlechat.accounts.${accountId}.dm.allowFrom`,
}
: {
policyKey: "channels.googlechat.dm.policy",
allowFromKey: "channels.googlechat.dm.allowFrom",
},
getCurrent: (cfg, accountId) =>
resolveGoogleChatAccount({
cfg,
accountId: accountId ?? DEFAULT_ACCOUNT_ID,
}).config.dm?.policy ?? "pairing",
setPolicy: (cfg, policy, accountId) => {
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
const currentDm = resolveGoogleChatAccount({
cfg,
accountId: resolvedAccountId,
}).config.dm;
return applySetupAccountConfigPatch({
cfg,
channelKey: channel,
accountId: resolvedAccountId,
patch: {
dm: {
...currentDm,
policy,
...(policy === "open"
? { allowFrom: addWildcardAllowFrom(currentDm?.allowFrom) }
: {}),
},
},
});
},
promptAllowFrom,
enabled: true,
});
};
export { googlechatSetupAdapter } from "./setup-core.js";

View File

@ -161,6 +161,66 @@ describe("googlechat setup", () => {
expect(result.cfg.channels?.googlechat?.audience).toBe("https://example.com/googlechat");
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
googlechatPlugin.setupWizard?.dmPolicy?.getCurrent(
{
channels: {
googlechat: {
dm: {
policy: "disabled",
},
accounts: {
alerts: {
serviceAccount: { client_email: "bot@example.com" },
dm: {
policy: "allowlist",
},
},
},
},
},
} as OpenClawConfig,
"alerts",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(googlechatPlugin.setupWizard?.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.googlechat.accounts.alerts.dm.policy",
allowFromKey: "channels.googlechat.accounts.alerts.dm.allowFrom",
});
});
it('writes open DM policy to the named account and preserves inherited allowFrom with "*"', () => {
const next = googlechatPlugin.setupWizard?.dmPolicy?.setPolicy(
{
channels: {
googlechat: {
dm: {
allowFrom: ["users/123"],
},
accounts: {
alerts: {
serviceAccount: { client_email: "bot@example.com" },
},
},
},
},
} as OpenClawConfig,
"open",
"alerts",
);
expect(next?.channels?.googlechat?.dm?.policy).toBeUndefined();
expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.policy).toBe("open");
expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.allowFrom).toEqual([
"users/123",
"*",
]);
});
it("keeps startAccount pending until abort, then unregisters", async () => {
const unregister = vi.fn();
hoisted.startGoogleChatMonitor.mockResolvedValue(unregister);

View File

@ -162,6 +162,59 @@ describe("telegramSetupWizard.finalize", () => {
});
});
describe("telegramSetupWizard.dmPolicy", () => {
it("reads the named-account DM policy instead of the channel root", () => {
expect(
telegramSetupWizard.dmPolicy?.getCurrent(
{
channels: {
telegram: {
dmPolicy: "disabled",
accounts: {
alerts: {
dmPolicy: "allowlist",
botToken: "tok",
},
},
},
},
},
"alerts",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(telegramSetupWizard.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.telegram.accounts.alerts.dmPolicy",
allowFromKey: "channels.telegram.accounts.alerts.allowFrom",
});
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const next = telegramSetupWizard.dmPolicy?.setPolicy(
{
channels: {
telegram: {
allowFrom: ["123"],
accounts: {
alerts: {
botToken: "tok",
},
},
},
},
},
"open",
"alerts",
);
expect(next?.channels?.telegram?.dmPolicy).toBeUndefined();
expect(next?.channels?.telegram?.accounts?.alerts?.dmPolicy).toBe("open");
expect(next?.channels?.telegram?.accounts?.alerts?.allowFrom).toEqual(["123", "*"]);
});
});
describe("resolveTelegramAllowFromEntries", () => {
it("passes apiRoot through username lookups", async () => {
const globalFetch = vi.fn(async () => {

View File

@ -1,7 +1,8 @@
import {
addWildcardAllowFrom,
createAllowFromSection,
createTopLevelChannelDmPolicy,
createStandardChannelSetupStatus,
type ChannelSetupDmPolicy,
DEFAULT_ACCOUNT_ID,
hasConfiguredSecretInput,
type OpenClawConfig,
@ -76,14 +77,40 @@ function buildTelegramDmAccessWarningLines(accountId: string): string[] {
];
}
const dmPolicy = createTopLevelChannelDmPolicy({
const dmPolicy: ChannelSetupDmPolicy = {
label: "Telegram",
channel,
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
getCurrent: (cfg) => cfg.channels?.telegram?.dmPolicy ?? "pairing",
resolveConfigKeys: (_cfg, accountId) =>
accountId && accountId !== DEFAULT_ACCOUNT_ID
? {
policyKey: `channels.telegram.accounts.${accountId}.dmPolicy`,
allowFromKey: `channels.telegram.accounts.${accountId}.allowFrom`,
}
: {
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
},
getCurrent: (cfg, accountId) =>
mergeTelegramAccountConfig(cfg, accountId ?? DEFAULT_ACCOUNT_ID).dmPolicy ?? "pairing",
setPolicy: (cfg, policy, accountId) => {
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
const merged = mergeTelegramAccountConfig(cfg, resolvedAccountId);
return patchChannelConfigForAccount({
cfg,
channel,
accountId: resolvedAccountId,
patch: {
dmPolicy: policy,
...(policy === "open"
? { allowFrom: addWildcardAllowFrom(merged.allowFrom) }
: {}),
},
});
},
promptAllowFrom: promptTelegramAllowFromForAccount,
});
};
export const telegramSetupWizard: ChannelSetupWizard = {
channel,