mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 12:28:37 +00:00
refactor: unify account-scoped dm security policy resolver
This commit is contained in:
@@ -1,4 +1,7 @@
|
|||||||
import { buildOpenGroupPolicyRestrictSendersWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import type {
|
import type {
|
||||||
ChannelAccountSnapshot,
|
ChannelAccountSnapshot,
|
||||||
ChannelPlugin,
|
ChannelPlugin,
|
||||||
@@ -12,7 +15,6 @@ import {
|
|||||||
collectBlueBubblesStatusIssues,
|
collectBlueBubblesStatusIssues,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
migrateBaseNameToDefaultAccount,
|
migrateBaseNameToDefaultAccount,
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
PAIRING_APPROVED_MESSAGE,
|
PAIRING_APPROVED_MESSAGE,
|
||||||
@@ -125,19 +127,16 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
|
|||||||
actions: bluebubblesMessageActions,
|
actions: bluebubblesMessageActions,
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.bluebubbles?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "bluebubbles",
|
||||||
? `channels.bluebubbles.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.bluebubbles.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("bluebubbles"),
|
|
||||||
normalizeEntry: (raw) => normalizeBlueBubblesHandle(raw.replace(/^bluebubbles:/i, "")),
|
normalizeEntry: (raw) => normalizeBlueBubblesHandle(raw.replace(/^bluebubbles:/i, "")),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account }) => {
|
collectWarnings: ({ account }) => {
|
||||||
const groupPolicy = account.config.groupPolicy ?? "allowlist";
|
const groupPolicy = account.config.groupPolicy ?? "allowlist";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
|
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
|
||||||
buildOpenGroupPolicyWarning,
|
buildOpenGroupPolicyWarning,
|
||||||
} from "openclaw/plugin-sdk";
|
} from "openclaw/plugin-sdk";
|
||||||
@@ -13,7 +14,6 @@ import {
|
|||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
discordOnboardingAdapter,
|
discordOnboardingAdapter,
|
||||||
DiscordConfigSchema,
|
DiscordConfigSchema,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
inspectDiscordAccount,
|
inspectDiscordAccount,
|
||||||
listDiscordAccountIds,
|
listDiscordAccountIds,
|
||||||
@@ -127,18 +127,16 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.discord?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const allowFromPath = useAccountPath
|
channelKey: "discord",
|
||||||
? `channels.discord.accounts.${resolvedAccountId}.dm.`
|
accountId,
|
||||||
: "channels.discord.dm.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dm?.policy,
|
||||||
policy: account.config.dm?.policy ?? "pairing",
|
|
||||||
allowFrom: account.config.dm?.allowFrom ?? [],
|
allowFrom: account.config.dm?.allowFrom ?? [],
|
||||||
allowFromPath,
|
allowFromPathSuffix: "dm.",
|
||||||
approveHint: formatPairingApproveHint("discord"),
|
|
||||||
normalizeEntry: (raw) => raw.replace(/^(discord|user):/i, "").replace(/^<@!?(\d+)>$/, "$1"),
|
normalizeEntry: (raw) => raw.replace(/^(discord|user):/i, "").replace(/^<@!?(\d+)>$/, "$1"),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { buildOpenGroupPolicyConfigureRouteAllowlistWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
applyAccountNameToChannelSection,
|
applyAccountNameToChannelSection,
|
||||||
applySetupAccountConfigPatch,
|
applySetupAccountConfigPatch,
|
||||||
@@ -6,7 +9,6 @@ import {
|
|||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
listDirectoryGroupEntriesFromMapKeys,
|
listDirectoryGroupEntriesFromMapKeys,
|
||||||
listDirectoryUserEntriesFromAllowFrom,
|
listDirectoryUserEntriesFromAllowFrom,
|
||||||
@@ -190,18 +192,16 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.["googlechat"]?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const allowFromPath = useAccountPath
|
channelKey: "googlechat",
|
||||||
? `channels.googlechat.accounts.${resolvedAccountId}.dm.`
|
accountId,
|
||||||
: "channels.googlechat.dm.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dm?.policy,
|
||||||
policy: account.config.dm?.policy ?? "pairing",
|
|
||||||
allowFrom: account.config.dm?.allowFrom ?? [],
|
allowFrom: account.config.dm?.allowFrom ?? [],
|
||||||
allowFromPath,
|
allowFromPathSuffix: "dm.",
|
||||||
approveHint: formatPairingApproveHint("googlechat"),
|
|
||||||
normalizeEntry: (raw) => formatAllowFromEntry(raw),
|
normalizeEntry: (raw) => formatAllowFromEntry(raw),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { buildOpenGroupPolicyRestrictSendersWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
applyAccountNameToChannelSection,
|
applyAccountNameToChannelSection,
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
collectStatusIssuesFromLastError,
|
collectStatusIssuesFromLastError,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
formatTrimmedAllowFromEntries,
|
formatTrimmedAllowFromEntries,
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
imessageOnboardingAdapter,
|
imessageOnboardingAdapter,
|
||||||
@@ -132,18 +134,15 @@ export const imessagePlugin: ChannelPlugin<ResolvedIMessageAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.imessage?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "imessage",
|
||||||
? `channels.imessage.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.imessage.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
});
|
||||||
approveHint: formatPairingApproveHint("imessage"),
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { buildOpenGroupPolicyWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
buildBaseAccountStatusSnapshot,
|
buildBaseAccountStatusSnapshot,
|
||||||
buildBaseChannelStatusSummary,
|
buildBaseChannelStatusSummary,
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
PAIRING_APPROVED_MESSAGE,
|
PAIRING_APPROVED_MESSAGE,
|
||||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||||
@@ -123,19 +125,16 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.irc?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "irc",
|
||||||
? `channels.irc.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.irc.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: `${basePath}allowFrom`,
|
|
||||||
approveHint: formatPairingApproveHint("irc"),
|
|
||||||
normalizeEntry: (raw) => normalizeIrcAllowEntry(raw),
|
normalizeEntry: (raw) => normalizeIrcAllowEntry(raw),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { buildOpenGroupPolicyRestrictSendersWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
buildComputedAccountStatusSnapshot,
|
buildComputedAccountStatusSnapshot,
|
||||||
@@ -159,21 +162,17 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(
|
cfg,
|
||||||
(cfg.channels?.line as LineConfig | undefined)?.accounts?.[resolvedAccountId],
|
channelKey: "line",
|
||||||
);
|
accountId,
|
||||||
const basePath = useAccountPath
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
? `channels.line.accounts.${resolvedAccountId}.`
|
policy: account.config.dmPolicy,
|
||||||
: "channels.line.";
|
|
||||||
return {
|
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: "openclaw pairing approve line <code>",
|
approveHint: "openclaw pairing approve line <code>",
|
||||||
normalizeEntry: (raw) => raw.replace(/^line:(?:user:)?/i, ""),
|
normalizeEntry: (raw) => raw.replace(/^line:(?:user:)?/i, ""),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { buildOpenGroupPolicyWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
applyAccountNameToChannelSection,
|
applyAccountNameToChannelSection,
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
@@ -6,7 +9,6 @@ import {
|
|||||||
collectStatusIssuesFromLastError,
|
collectStatusIssuesFromLastError,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
PAIRING_APPROVED_MESSAGE,
|
PAIRING_APPROVED_MESSAGE,
|
||||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||||
@@ -159,20 +161,17 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
|
|||||||
formatAllowFrom: ({ allowFrom }) => normalizeMatrixAllowList(allowFrom),
|
formatAllowFrom: ({ allowFrom }) => normalizeMatrixAllowList(allowFrom),
|
||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const accountId = account.accountId;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const prefix =
|
cfg: cfg as CoreConfig,
|
||||||
accountId && accountId !== "default"
|
channelKey: "matrix",
|
||||||
? `channels.matrix.accounts.${accountId}.dm`
|
accountId,
|
||||||
: "channels.matrix.dm";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dm?.policy,
|
||||||
policy: account.config.dm?.policy ?? "pairing",
|
|
||||||
allowFrom: account.config.dm?.allowFrom ?? [],
|
allowFrom: account.config.dm?.allowFrom ?? [],
|
||||||
policyPath: `${prefix}.policy`,
|
allowFromPathSuffix: "dm.",
|
||||||
allowFromPath: `${prefix}.allowFrom`,
|
|
||||||
approveHint: formatPairingApproveHint("matrix"),
|
|
||||||
normalizeEntry: (raw) => normalizeMatrixUserId(raw),
|
normalizeEntry: (raw) => normalizeMatrixUserId(raw),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg as CoreConfig);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg as CoreConfig);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { buildOpenGroupPolicyRestrictSendersWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
applyAccountNameToChannelSection,
|
applyAccountNameToChannelSection,
|
||||||
applySetupAccountConfigPatch,
|
applySetupAccountConfigPatch,
|
||||||
@@ -6,7 +9,6 @@ import {
|
|||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
migrateBaseNameToDefaultAccount,
|
migrateBaseNameToDefaultAccount,
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||||
@@ -281,19 +283,16 @@ export const mattermostPlugin: ChannelPlugin<ResolvedMattermostAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.mattermost?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "mattermost",
|
||||||
? `channels.mattermost.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.mattermost.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("mattermost"),
|
|
||||||
normalizeEntry: (raw) => normalizeAllowEntry(raw),
|
normalizeEntry: (raw) => normalizeAllowEntry(raw),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
buildOpenGroupPolicyNoRouteAllowlistWarning,
|
buildOpenGroupPolicyNoRouteAllowlistWarning,
|
||||||
buildOpenGroupPolicyRestrictSendersWarning,
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
} from "openclaw/plugin-sdk";
|
} from "openclaw/plugin-sdk";
|
||||||
@@ -10,7 +11,6 @@ import {
|
|||||||
clearAccountEntryFields,
|
clearAccountEntryFields,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||||
resolveDefaultGroupPolicy,
|
resolveDefaultGroupPolicy,
|
||||||
@@ -121,21 +121,16 @@ export const nextcloudTalkPlugin: ChannelPlugin<ResolvedNextcloudTalkAccount> =
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(
|
cfg,
|
||||||
cfg.channels?.["nextcloud-talk"]?.accounts?.[resolvedAccountId],
|
channelKey: "nextcloud-talk",
|
||||||
);
|
accountId,
|
||||||
const basePath = useAccountPath
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
? `channels.nextcloud-talk.accounts.${resolvedAccountId}.`
|
policy: account.config.dmPolicy,
|
||||||
: "channels.nextcloud-talk.";
|
|
||||||
return {
|
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("nextcloud-talk"),
|
|
||||||
normalizeEntry: (raw) => raw.replace(/^(nextcloud-talk|nc-talk|nc):/i, "").toLowerCase(),
|
normalizeEntry: (raw) => raw.replace(/^(nextcloud-talk|nc-talk|nc):/i, "").toLowerCase(),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { buildOpenGroupPolicyRestrictSendersWarning } from "openclaw/plugin-sdk";
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
applyAccountNameToChannelSection,
|
applyAccountNameToChannelSection,
|
||||||
buildBaseAccountStatusSnapshot,
|
buildBaseAccountStatusSnapshot,
|
||||||
@@ -8,7 +11,6 @@ import {
|
|||||||
createDefaultChannelRuntimeState,
|
createDefaultChannelRuntimeState,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
listSignalAccountIds,
|
listSignalAccountIds,
|
||||||
looksLikeSignalTargetId,
|
looksLikeSignalTargetId,
|
||||||
@@ -155,19 +157,16 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.signal?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "signal",
|
||||||
? `channels.signal.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.signal.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("signal"),
|
|
||||||
normalizeEntry: (raw) => normalizeE164(raw.replace(/^signal:/i, "").trim()),
|
normalizeEntry: (raw) => normalizeE164(raw.replace(/^signal:/i, "").trim()),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
|
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
|
||||||
buildOpenGroupPolicyWarning,
|
buildOpenGroupPolicyWarning,
|
||||||
} from "openclaw/plugin-sdk";
|
} from "openclaw/plugin-sdk";
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
extractSlackToolSend,
|
extractSlackToolSend,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
handleSlackMessageAction,
|
handleSlackMessageAction,
|
||||||
inspectSlackAccount,
|
inspectSlackAccount,
|
||||||
@@ -177,18 +177,16 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.slack?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const allowFromPath = useAccountPath
|
channelKey: "slack",
|
||||||
? `channels.slack.accounts.${resolvedAccountId}.dm.`
|
accountId,
|
||||||
: "channels.slack.dm.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.dm?.policy,
|
||||||
policy: account.dm?.policy ?? "pairing",
|
|
||||||
allowFrom: account.dm?.allowFrom ?? [],
|
allowFrom: account.dm?.allowFrom ?? [],
|
||||||
allowFromPath,
|
allowFromPathSuffix: "dm.",
|
||||||
approveHint: formatPairingApproveHint("slack"),
|
|
||||||
normalizeEntry: (raw) => raw.replace(/^(slack|user):/i, ""),
|
normalizeEntry: (raw) => raw.replace(/^(slack|user):/i, ""),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
buildOpenGroupPolicyNoRouteAllowlistWarning,
|
buildOpenGroupPolicyNoRouteAllowlistWarning,
|
||||||
buildOpenGroupPolicyRestrictSendersWarning,
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
} from "openclaw/plugin-sdk";
|
} from "openclaw/plugin-sdk";
|
||||||
@@ -10,7 +11,6 @@ import {
|
|||||||
collectTelegramStatusIssues,
|
collectTelegramStatusIssues,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
inspectTelegramAccount,
|
inspectTelegramAccount,
|
||||||
listTelegramAccountIds,
|
listTelegramAccountIds,
|
||||||
@@ -192,19 +192,16 @@ export const telegramPlugin: ChannelPlugin<ResolvedTelegramAccount, TelegramProb
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.telegram?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "telegram",
|
||||||
? `channels.telegram.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.telegram.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("telegram"),
|
|
||||||
normalizeEntry: (raw) => raw.replace(/^(telegram|tg):/i, ""),
|
normalizeEntry: (raw) => raw.replace(/^(telegram|tg):/i, ""),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
buildOpenGroupPolicyNoRouteAllowlistWarning,
|
buildOpenGroupPolicyNoRouteAllowlistWarning,
|
||||||
buildOpenGroupPolicyRestrictSendersWarning,
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
} from "openclaw/plugin-sdk";
|
} from "openclaw/plugin-sdk";
|
||||||
@@ -8,7 +9,6 @@ import {
|
|||||||
collectWhatsAppStatusIssues,
|
collectWhatsAppStatusIssues,
|
||||||
createActionGate,
|
createActionGate,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
formatPairingApproveHint,
|
|
||||||
getChatChannelMeta,
|
getChatChannelMeta,
|
||||||
listWhatsAppAccountIds,
|
listWhatsAppAccountIds,
|
||||||
listWhatsAppDirectoryGroupsFromConfig,
|
listWhatsAppDirectoryGroupsFromConfig,
|
||||||
@@ -125,19 +125,16 @@ export const whatsappPlugin: ChannelPlugin<ResolvedWhatsAppAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const useAccountPath = Boolean(cfg.channels?.whatsapp?.accounts?.[resolvedAccountId]);
|
cfg,
|
||||||
const basePath = useAccountPath
|
channelKey: "whatsapp",
|
||||||
? `channels.whatsapp.accounts.${resolvedAccountId}.`
|
accountId,
|
||||||
: "channels.whatsapp.";
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.dmPolicy,
|
||||||
policy: account.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.allowFrom ?? [],
|
allowFrom: account.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("whatsapp"),
|
|
||||||
normalizeEntry: (raw) => normalizeE164(raw),
|
normalizeEntry: (raw) => normalizeE164(raw),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
buildOpenGroupPolicyRestrictSendersWarning,
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
buildOpenGroupPolicyWarning,
|
buildOpenGroupPolicyWarning,
|
||||||
} from "openclaw/plugin-sdk";
|
} from "openclaw/plugin-sdk";
|
||||||
@@ -19,7 +20,6 @@ import {
|
|||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
chunkTextForOutbound,
|
chunkTextForOutbound,
|
||||||
formatAllowFromLowercase,
|
formatAllowFromLowercase,
|
||||||
formatPairingApproveHint,
|
|
||||||
migrateBaseNameToDefaultAccount,
|
migrateBaseNameToDefaultAccount,
|
||||||
listDirectoryUserEntriesFromAllowFrom,
|
listDirectoryUserEntriesFromAllowFrom,
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
resolveOutboundMediaUrls,
|
resolveOutboundMediaUrls,
|
||||||
resolveDefaultGroupPolicy,
|
resolveDefaultGroupPolicy,
|
||||||
resolveOpenProviderRuntimeGroupPolicy,
|
resolveOpenProviderRuntimeGroupPolicy,
|
||||||
resolveChannelAccountConfigBasePath,
|
|
||||||
sendPayloadWithChunkedTextAndMedia,
|
sendPayloadWithChunkedTextAndMedia,
|
||||||
setAccountEnabledInConfigSection,
|
setAccountEnabledInConfigSection,
|
||||||
} from "openclaw/plugin-sdk/zalo";
|
} from "openclaw/plugin-sdk/zalo";
|
||||||
@@ -142,20 +141,16 @@ export const zaloPlugin: ChannelPlugin<ResolvedZaloAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const basePath = resolveChannelAccountConfigBasePath({
|
|
||||||
cfg,
|
cfg,
|
||||||
channelKey: "zalo",
|
channelKey: "zalo",
|
||||||
accountId: resolvedAccountId,
|
accountId,
|
||||||
});
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("zalo"),
|
|
||||||
normalizeEntry: (raw) => raw.replace(/^(zalo|zl):/i, ""),
|
normalizeEntry: (raw) => raw.replace(/^(zalo|zl):/i, ""),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
collectWarnings: ({ account, cfg }) => {
|
collectWarnings: ({ account, cfg }) => {
|
||||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { buildAccountScopedDmSecurityPolicy } from "openclaw/plugin-sdk";
|
||||||
import type {
|
import type {
|
||||||
ChannelAccountSnapshot,
|
ChannelAccountSnapshot,
|
||||||
ChannelDirectoryEntry,
|
ChannelDirectoryEntry,
|
||||||
@@ -18,11 +19,9 @@ import {
|
|||||||
chunkTextForOutbound,
|
chunkTextForOutbound,
|
||||||
deleteAccountFromConfigSection,
|
deleteAccountFromConfigSection,
|
||||||
formatAllowFromLowercase,
|
formatAllowFromLowercase,
|
||||||
formatPairingApproveHint,
|
|
||||||
isNumericTargetId,
|
isNumericTargetId,
|
||||||
migrateBaseNameToDefaultAccount,
|
migrateBaseNameToDefaultAccount,
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
resolveChannelAccountConfigBasePath,
|
|
||||||
sendPayloadWithChunkedTextAndMedia,
|
sendPayloadWithChunkedTextAndMedia,
|
||||||
setAccountEnabledInConfigSection,
|
setAccountEnabledInConfigSection,
|
||||||
} from "openclaw/plugin-sdk/zalouser";
|
} from "openclaw/plugin-sdk/zalouser";
|
||||||
@@ -282,20 +281,16 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
return buildAccountScopedDmSecurityPolicy({
|
||||||
const basePath = resolveChannelAccountConfigBasePath({
|
|
||||||
cfg,
|
cfg,
|
||||||
channelKey: "zalouser",
|
channelKey: "zalouser",
|
||||||
accountId: resolvedAccountId,
|
accountId,
|
||||||
});
|
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
||||||
return {
|
policy: account.config.dmPolicy,
|
||||||
policy: account.config.dmPolicy ?? "pairing",
|
|
||||||
allowFrom: account.config.allowFrom ?? [],
|
allowFrom: account.config.allowFrom ?? [],
|
||||||
policyPath: `${basePath}dmPolicy`,
|
policyPathSuffix: "dmPolicy",
|
||||||
allowFromPath: basePath,
|
|
||||||
approveHint: formatPairingApproveHint("zalouser"),
|
|
||||||
normalizeEntry: (raw) => raw.replace(/^(zalouser|zlu):/i, ""),
|
normalizeEntry: (raw) => raw.replace(/^(zalouser|zlu):/i, ""),
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
|
|||||||
95
src/channels/plugins/helpers.test.ts
Normal file
95
src/channels/plugins/helpers.test.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../../config/config.js";
|
||||||
|
import { buildAccountScopedDmSecurityPolicy, formatPairingApproveHint } from "./helpers.js";
|
||||||
|
|
||||||
|
function cfgWithChannel(channelKey: string, accounts?: Record<string, unknown>): OpenClawConfig {
|
||||||
|
return {
|
||||||
|
channels: {
|
||||||
|
[channelKey]: accounts ? { accounts } : {},
|
||||||
|
},
|
||||||
|
} as unknown as OpenClawConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("buildAccountScopedDmSecurityPolicy", () => {
|
||||||
|
it("builds top-level dm policy paths when no account config exists", () => {
|
||||||
|
expect(
|
||||||
|
buildAccountScopedDmSecurityPolicy({
|
||||||
|
cfg: cfgWithChannel("telegram"),
|
||||||
|
channelKey: "telegram",
|
||||||
|
fallbackAccountId: "default",
|
||||||
|
policy: "pairing",
|
||||||
|
allowFrom: ["123"],
|
||||||
|
policyPathSuffix: "dmPolicy",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
policy: "pairing",
|
||||||
|
allowFrom: ["123"],
|
||||||
|
policyPath: "channels.telegram.dmPolicy",
|
||||||
|
allowFromPath: "channels.telegram.",
|
||||||
|
approveHint: formatPairingApproveHint("telegram"),
|
||||||
|
normalizeEntry: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses account-scoped paths when account config exists", () => {
|
||||||
|
expect(
|
||||||
|
buildAccountScopedDmSecurityPolicy({
|
||||||
|
cfg: cfgWithChannel("signal", { work: {} }),
|
||||||
|
channelKey: "signal",
|
||||||
|
accountId: "work",
|
||||||
|
fallbackAccountId: "default",
|
||||||
|
policy: "allowlist",
|
||||||
|
allowFrom: ["+12125551212"],
|
||||||
|
policyPathSuffix: "dmPolicy",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
policy: "allowlist",
|
||||||
|
allowFrom: ["+12125551212"],
|
||||||
|
policyPath: "channels.signal.accounts.work.dmPolicy",
|
||||||
|
allowFromPath: "channels.signal.accounts.work.",
|
||||||
|
approveHint: formatPairingApproveHint("signal"),
|
||||||
|
normalizeEntry: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports nested dm paths without explicit policyPath", () => {
|
||||||
|
expect(
|
||||||
|
buildAccountScopedDmSecurityPolicy({
|
||||||
|
cfg: cfgWithChannel("discord", { work: {} }),
|
||||||
|
channelKey: "discord",
|
||||||
|
accountId: "work",
|
||||||
|
policy: "pairing",
|
||||||
|
allowFrom: [],
|
||||||
|
allowFromPathSuffix: "dm.",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
policy: "pairing",
|
||||||
|
allowFrom: [],
|
||||||
|
policyPath: undefined,
|
||||||
|
allowFromPath: "channels.discord.accounts.work.dm.",
|
||||||
|
approveHint: formatPairingApproveHint("discord"),
|
||||||
|
normalizeEntry: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports custom defaults and approve hints", () => {
|
||||||
|
expect(
|
||||||
|
buildAccountScopedDmSecurityPolicy({
|
||||||
|
cfg: cfgWithChannel("synology-chat"),
|
||||||
|
channelKey: "synology-chat",
|
||||||
|
fallbackAccountId: "default",
|
||||||
|
allowFrom: ["user-1"],
|
||||||
|
defaultPolicy: "allowlist",
|
||||||
|
policyPathSuffix: "dmPolicy",
|
||||||
|
approveHint: "openclaw pairing approve synology-chat <code>",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
policy: "allowlist",
|
||||||
|
allowFrom: ["user-1"],
|
||||||
|
policyPath: "channels.synology-chat.dmPolicy",
|
||||||
|
allowFromPath: "channels.synology-chat.",
|
||||||
|
approveHint: "openclaw pairing approve synology-chat <code>",
|
||||||
|
normalizeEntry: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { formatCliCommand } from "../../cli/command-format.js";
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import type { OpenClawConfig } from "../../config/config.js";
|
import type { OpenClawConfig } from "../../config/config.js";
|
||||||
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
||||||
|
import type { ChannelSecurityDmPolicy } from "./types.core.js";
|
||||||
import type { ChannelPlugin } from "./types.js";
|
import type { ChannelPlugin } from "./types.js";
|
||||||
|
|
||||||
// Channel docking helper: use this when selecting the default account for a plugin.
|
// Channel docking helper: use this when selecting the default account for a plugin.
|
||||||
@@ -18,3 +19,40 @@ export function formatPairingApproveHint(channelId: string): string {
|
|||||||
const approveCmd = formatCliCommand(`openclaw pairing approve ${channelId} <code>`);
|
const approveCmd = formatCliCommand(`openclaw pairing approve ${channelId} <code>`);
|
||||||
return `Approve via: ${listCmd} / ${approveCmd}`;
|
return `Approve via: ${listCmd} / ${approveCmd}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildAccountScopedDmSecurityPolicy(params: {
|
||||||
|
cfg: OpenClawConfig;
|
||||||
|
channelKey: string;
|
||||||
|
accountId?: string | null;
|
||||||
|
fallbackAccountId?: string | null;
|
||||||
|
policy?: string | null;
|
||||||
|
allowFrom?: Array<string | number> | null;
|
||||||
|
defaultPolicy?: string;
|
||||||
|
allowFromPathSuffix?: string;
|
||||||
|
policyPathSuffix?: string;
|
||||||
|
approveChannelId?: string;
|
||||||
|
approveHint?: string;
|
||||||
|
normalizeEntry?: (raw: string) => string;
|
||||||
|
}): ChannelSecurityDmPolicy {
|
||||||
|
const resolvedAccountId = params.accountId ?? params.fallbackAccountId ?? DEFAULT_ACCOUNT_ID;
|
||||||
|
const channelConfig = (params.cfg.channels as Record<string, unknown> | undefined)?.[
|
||||||
|
params.channelKey
|
||||||
|
] as { accounts?: Record<string, unknown> } | undefined;
|
||||||
|
const useAccountPath = Boolean(channelConfig?.accounts?.[resolvedAccountId]);
|
||||||
|
const basePath = useAccountPath
|
||||||
|
? `channels.${params.channelKey}.accounts.${resolvedAccountId}.`
|
||||||
|
: `channels.${params.channelKey}.`;
|
||||||
|
const allowFromPath = `${basePath}${params.allowFromPathSuffix ?? ""}`;
|
||||||
|
const policyPath =
|
||||||
|
params.policyPathSuffix != null ? `${basePath}${params.policyPathSuffix}` : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
policy: params.policy ?? params.defaultPolicy ?? "pairing",
|
||||||
|
allowFrom: params.allowFrom ?? [],
|
||||||
|
policyPath,
|
||||||
|
allowFromPath,
|
||||||
|
approveHint:
|
||||||
|
params.approveHint ?? formatPairingApproveHint(params.approveChannelId ?? params.channelKey),
|
||||||
|
normalizeEntry: params.normalizeEntry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -537,7 +537,10 @@ export {
|
|||||||
buildOpenGroupPolicyRestrictSendersWarning,
|
buildOpenGroupPolicyRestrictSendersWarning,
|
||||||
buildOpenGroupPolicyWarning,
|
buildOpenGroupPolicyWarning,
|
||||||
} from "../channels/plugins/group-policy-warnings.js";
|
} from "../channels/plugins/group-policy-warnings.js";
|
||||||
export { formatPairingApproveHint } from "../channels/plugins/helpers.js";
|
export {
|
||||||
|
buildAccountScopedDmSecurityPolicy,
|
||||||
|
formatPairingApproveHint,
|
||||||
|
} from "../channels/plugins/helpers.js";
|
||||||
export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js";
|
export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|||||||
Reference in New Issue
Block a user