feat(mcp): add resource group scopes with permission inheritance
This commit is contained in:
31 files changed
+2398
-1047
No files matched your search
@@ -493,8 +493,10 @@ describe("normalizeMcpGlobalPolicy", () => {
|
||||
readOnly: false,
|
||||
allowDangerousSql: false,
|
||||
allowedConnectionIds: null,
|
||||
allowedGroupIds: [],
|
||||
allowedToolNames: null,
|
||||
connectionPolicies: [],
|
||||
groupPolicies: [],
|
||||
configured: false,
|
||||
queryTimeoutSecs: null,
|
||||
});
|
||||
@@ -512,8 +514,10 @@ describe("normalizeMcpGlobalPolicy", () => {
|
||||
readOnly: true,
|
||||
allowDangerousSql: true,
|
||||
allowedConnectionIds: ["connection-1", "connection-2"],
|
||||
allowedGroupIds: [],
|
||||
allowedToolNames: null,
|
||||
connectionPolicies: [],
|
||||
groupPolicies: [],
|
||||
configured: true,
|
||||
queryTimeoutSecs: null,
|
||||
});
|
||||
@@ -811,8 +815,10 @@ describe("settingsStore MCP policy persistence", () => {
|
||||
readOnly: true,
|
||||
allowDangerousSql: false,
|
||||
allowedConnectionIds: ["connection-1"],
|
||||
allowedGroupIds: [],
|
||||
allowedToolNames: null,
|
||||
connectionPolicies: [],
|
||||
groupPolicies: [],
|
||||
configured: true,
|
||||
queryTimeoutSecs: null,
|
||||
};
|
||||
@@ -826,8 +832,10 @@ describe("settingsStore MCP policy persistence", () => {
|
||||
readOnly: false,
|
||||
allowDangerousSql: false,
|
||||
allowedConnectionIds: [],
|
||||
allowedGroupIds: [],
|
||||
allowedToolNames: null,
|
||||
connectionPolicies: [],
|
||||
groupPolicies: [],
|
||||
configured: true,
|
||||
queryTimeoutSecs: null,
|
||||
});
|
||||
|
||||
@@ -53,13 +53,21 @@ export interface McpGlobalPolicy {
|
||||
readOnly: boolean;
|
||||
allowDangerousSql: boolean;
|
||||
allowedConnectionIds: string[] | null;
|
||||
allowedGroupIds: string[];
|
||||
allowedToolNames: string[] | null;
|
||||
connectionPolicies: McpConnectionPolicy[];
|
||||
groupPolicies: McpGroupPolicy[];
|
||||
configured: boolean;
|
||||
/** MCP query timeout override in seconds. null/undefined = inherit the connection; 0 = no limit. */
|
||||
queryTimeoutSecs: number | null;
|
||||
}
|
||||
|
||||
export interface McpGroupPolicy {
|
||||
groupId: string;
|
||||
readOnly: boolean;
|
||||
allowDangerousSql: boolean;
|
||||
}
|
||||
|
||||
export interface McpConnectionPolicy {
|
||||
connectionId: string;
|
||||
readOnly: boolean;
|
||||
@@ -113,14 +121,17 @@ export const DEFAULT_MCP_GLOBAL_POLICY: McpGlobalPolicy = {
|
||||
readOnly: false,
|
||||
allowDangerousSql: false,
|
||||
allowedConnectionIds: null,
|
||||
allowedGroupIds: [],
|
||||
allowedToolNames: null,
|
||||
connectionPolicies: [],
|
||||
groupPolicies: [],
|
||||
configured: false,
|
||||
queryTimeoutSecs: null,
|
||||
};
|
||||
|
||||
export function normalizeMcpGlobalPolicy(policy: Partial<McpGlobalPolicy> | null | undefined): McpGlobalPolicy {
|
||||
const allowedConnectionIds = policy?.allowedConnectionIds === null || policy?.allowedConnectionIds === undefined ? null : [...new Set(policy.allowedConnectionIds.filter((id): id is string => typeof id === "string" && id.trim().length > 0).map((id) => id.trim()))];
|
||||
const allowedGroupIds = allowedConnectionIds === null ? [] : [...new Set((policy?.allowedGroupIds ?? []).filter((id): id is string => typeof id === "string" && id.trim().length > 0).map((id) => id.trim()))];
|
||||
const allowedToolNames = policy?.allowedToolNames === null || policy?.allowedToolNames === undefined ? null : [...new Set(policy.allowedToolNames.filter((name): name is string => typeof name === "string" && name.trim().length > 0).map((name) => name.trim()))];
|
||||
const connectionPolicies = Object.values(
|
||||
(policy?.connectionPolicies ?? []).reduce<Record<string, McpConnectionPolicy>>((rules, rule) => {
|
||||
@@ -156,6 +167,20 @@ export function normalizeMcpGlobalPolicy(policy: Partial<McpGlobalPolicy> | null
|
||||
return rules;
|
||||
}, {}),
|
||||
);
|
||||
const groupPolicies = Object.values(
|
||||
(policy?.groupPolicies ?? []).reduce<Record<string, McpGroupPolicy>>((rules, rule) => {
|
||||
if (!rule || typeof rule.groupId !== "string" || !rule.groupId.trim()) return rules;
|
||||
const groupId = rule.groupId.trim();
|
||||
const current = rules[groupId];
|
||||
const readOnly = current?.readOnly === true || rule.readOnly === true;
|
||||
rules[groupId] = {
|
||||
groupId,
|
||||
readOnly,
|
||||
allowDangerousSql: !readOnly && (current ? current.allowDangerousSql && rule.allowDangerousSql === true : rule.allowDangerousSql === true),
|
||||
};
|
||||
return rules;
|
||||
}, {}),
|
||||
);
|
||||
// null / undefined / non-positive => null (inherit connection). 0 is preserved
|
||||
// as an explicit "no limit" only here; the UI maps "no limit" <=> 0 and
|
||||
// "inherit" <=> null.
|
||||
@@ -164,8 +189,10 @@ export function normalizeMcpGlobalPolicy(policy: Partial<McpGlobalPolicy> | null
|
||||
readOnly: policy?.readOnly === true,
|
||||
allowDangerousSql: policy?.allowDangerousSql === true,
|
||||
allowedConnectionIds,
|
||||
allowedGroupIds,
|
||||
allowedToolNames,
|
||||
connectionPolicies,
|
||||
groupPolicies,
|
||||
configured: policy?.configured === true,
|
||||
queryTimeoutSecs,
|
||||
};
|
||||
@@ -1783,8 +1810,10 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
readOnly: next.readOnly,
|
||||
allowDangerousSql: next.allowDangerousSql,
|
||||
allowedConnectionIds: next.allowedConnectionIds,
|
||||
allowedGroupIds: next.allowedGroupIds,
|
||||
allowedToolNames: next.allowedToolNames,
|
||||
connectionPolicies: next.connectionPolicies,
|
||||
groupPolicies: next.groupPolicies,
|
||||
queryTimeoutSecs: next.queryTimeoutSecs,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in new issue
Block a user