mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-01 10:23:36 +00:00
fix(feishu): keep top-level appSecret SecretRef active for the implicit default account (#96965)
When a Feishu channel has top-level appId/appSecret using SecretRef format and sub-accounts with their own inline appSecret, the secrets resolver marks the top-level SecretRef as inactive because isBaseFieldActiveForChannelSurface only checks whether any explicit account inherits the field. Feishu account listing always creates an implicit default account from top-level credentials. The fix detects this implicit default account and keeps the top-level appSecret active accordingly, without changing the shared channel secret helper semantics for other channels. Fixes #96929 Signed-off-by: 赵旺0668001248 <0668001248@duomai.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
// Feishu plugin module implements secret contract behavior.
|
||||
import {
|
||||
collectConditionalChannelFieldAssignments,
|
||||
collectSimpleChannelFieldAssignments,
|
||||
collectSecretInputAssignment,
|
||||
getChannelSurface,
|
||||
hasConfiguredSecretInputValue,
|
||||
hasOwnProperty,
|
||||
isBaseFieldActiveForChannelSurface,
|
||||
normalizeSecretStringValue,
|
||||
type ResolverContext,
|
||||
type SecretDefaults,
|
||||
@@ -89,16 +91,48 @@ export function collectRuntimeConfigAssignments(params: {
|
||||
return;
|
||||
}
|
||||
const { channel: feishu, surface } = resolved;
|
||||
collectSimpleChannelFieldAssignments({
|
||||
channelKey: "feishu",
|
||||
field: "appSecret",
|
||||
channel: feishu,
|
||||
surface,
|
||||
// Feishu account listing starts an implicit default account from top-level
|
||||
// appId+appSecret even when every named account overrides appSecret. The
|
||||
// shared helper's isBaseFieldActiveForChannelSurface only checks whether any
|
||||
// explicit account inherits the field, so top-level appSecret refs would be
|
||||
// skipped when all accounts override. Account for the implicit default here.
|
||||
const hasImplicitDefaultAccount =
|
||||
surface.channelEnabled &&
|
||||
hasConfiguredSecretInputValue(feishu.appId, params.defaults) &&
|
||||
hasConfiguredSecretInputValue(feishu.appSecret, params.defaults);
|
||||
const topLevelAppSecretActive =
|
||||
hasImplicitDefaultAccount || isBaseFieldActiveForChannelSurface(surface, "appSecret");
|
||||
collectSecretInputAssignment({
|
||||
value: feishu.appSecret,
|
||||
path: "channels.feishu.appSecret",
|
||||
expected: "string",
|
||||
defaults: params.defaults,
|
||||
context: params.context,
|
||||
topInactiveReason: "no enabled account inherits this top-level Feishu appSecret.",
|
||||
accountInactiveReason: "Feishu account is disabled.",
|
||||
active: topLevelAppSecretActive,
|
||||
inactiveReason: "no enabled account inherits this top-level Feishu appSecret.",
|
||||
apply: (value) => {
|
||||
feishu.appSecret = value;
|
||||
},
|
||||
});
|
||||
if (surface.hasExplicitAccounts) {
|
||||
for (const { accountId, account, enabled } of surface.accounts) {
|
||||
if (!hasOwnProperty(account, "appSecret")) {
|
||||
continue;
|
||||
}
|
||||
collectSecretInputAssignment({
|
||||
value: account.appSecret,
|
||||
path: `channels.feishu.accounts.${accountId}.appSecret`,
|
||||
expected: "string",
|
||||
defaults: params.defaults,
|
||||
context: params.context,
|
||||
active: enabled,
|
||||
inactiveReason: "Feishu account is disabled.",
|
||||
apply: (value) => {
|
||||
account.appSecret = value;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
const baseConnectionMode =
|
||||
normalizeSecretStringValue(feishu.connectionMode) === "webhook" ? "webhook" : "websocket";
|
||||
const resolveAccountMode = (account: Record<string, unknown>) =>
|
||||
|
||||
@@ -416,7 +416,9 @@ describe("secrets runtime externalized channel SecretRef audit", () => {
|
||||
enabled: true,
|
||||
tts: {
|
||||
providers: {
|
||||
openai: { apiKey: inactiveExecRef("DISCORD_DISABLED_VOICE_TTS_API_KEY") },
|
||||
openai: {
|
||||
apiKey: inactiveExecRef("DISCORD_DISABLED_VOICE_TTS_API_KEY"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -541,4 +543,36 @@ describe("secrets runtime externalized channel SecretRef audit", () => {
|
||||
]);
|
||||
expectMetadataBackedContractsWereUsed();
|
||||
});
|
||||
|
||||
it("resolves Feishu top-level appSecret SecretRef for the implicit default account", async () => {
|
||||
const records = configureExternalChannelRecords(["feishu"]);
|
||||
const snapshot = await prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
channels: {
|
||||
feishu: {
|
||||
enabled: true,
|
||||
appId: "cli_default",
|
||||
appSecret: ref("FEISHU_APP_SECRET"),
|
||||
accounts: {
|
||||
"resource-shrimp": {
|
||||
enabled: true,
|
||||
appId: "cli_resource",
|
||||
appSecret: "inline-secret-here", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: { FEISHU_APP_SECRET: "default-secret" },
|
||||
includeAuthStoreRefs: false,
|
||||
loadablePluginOrigins: externalChannelOrigins(records),
|
||||
});
|
||||
|
||||
expectResolvedPaths(snapshot.config, {
|
||||
"channels.feishu.appSecret": "default-secret",
|
||||
"channels.feishu.accounts.resource-shrimp.appSecret": "inline-secret-here",
|
||||
});
|
||||
expect(snapshot.warnings).toStrictEqual([]);
|
||||
expectMetadataBackedContractsWereUsed(["feishu"]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user