Lock fixed values in the tool editor for anyone but the owner

The server now refuses a fixed value or its filled-by-AI switch from team
editors, so the editor shows them locked instead of failing the save.
This commit is contained in:
arc53-machine committed 2026-09-29 14:17:35 +01:00
1 parent b2d26c72ed
commit 793b8cb520
2 files changed
+40 -5

No files matched your search

+24
View File
@@ -567,6 +567,30 @@ describe('ToolConfig', () => {
expect(credential && disabled(credential)).toBe(true);
});
it('locks fixed values for anyone but the owner', async () => {
// Even an editor who may change credentials: the server refuses a fixed
// value from anyone but the owner.
await render({
...userTool,
access: 'editor',
allowed_actions: ['edit', 'edit_credentials', 'use', 'use_in_own'],
} as UserToolType);
await act(async () => {
(
container.querySelector('[class*="cursor-pointer"]') as HTMLElement
).click();
});
const filled = container.querySelector(
'[aria-label="settings.tools.filledByLLM"]',
);
expect(filled?.hasAttribute('disabled')).toBe(true);
const tableInputs = Array.from(
container.querySelectorAll<HTMLInputElement>('table input[data-slot]'),
);
// The description stays editable; the value is the fixed one.
expect(tableInputs.at(-1)?.disabled).toBe(true);
});
it('says why the credentials are locked for an editor without edit_credentials', async () => {
await render({ ...configTool, ...editorNoCreds } as UserToolType);
const note = container.querySelector('[data-slot="alert"]');
+16 -5
View File
@@ -58,11 +58,14 @@ const BODY_TYPE_HINT_KEYS: Record<string, string> = {
/**
* What the caller may change on the open tool (`utils/accessUtils` `can`):
* `canEdit` covers the name and the actions, `canEditCredentials` the
* secrets, URLs and header / query values.
* secrets, URLs and header / query values, and `canFixValues` whether a
* parameter is filled by the AI or fixed, and its fixed value (the owner's
* alone: the server refuses anyone else).
*/
const ToolAccessContext = React.createContext({
canEdit: true,
canEditCredentials: true,
canFixValues: true,
});
/** Maps a body content type to its hint's locale key suffix (JSON by default). */
@@ -133,9 +136,10 @@ export default function ToolConfig({
can(tool, 'edit_credentials') && !sharedOAuth && !ownerOnlyConnection;
// Neither: the tool opens as a read-only view with no Save.
const readOnly = !canEdit && !canEditCredentials;
const canFixValues = isOwner(tool);
const access = React.useMemo(
() => ({ canEdit, canEditCredentials }),
[canEdit, canEditCredentials],
() => ({ canEdit, canEditCredentials, canFixValues }),
[canEdit, canEditCredentials, canFixValues],
);
const toggleUserActionExpand = (index: number) => {
@@ -681,6 +685,7 @@ export default function ToolConfig({
<Checkbox
size="sm"
checked={param[1].filled_by_llm}
disabled={!canFixValues}
id={uniqueKey}
aria-label={t(
'settings.tools.filledByLLM',
@@ -738,7 +743,10 @@ export default function ToolConfig({
<Input
value={param[1].value}
key={uniqueKey}
disabled={param[1].filled_by_llm}
disabled={
param[1].filled_by_llm ||
!canFixValues
}
size="sm"
onChange={(e) => {
setTool({
@@ -1293,7 +1301,8 @@ function APIActionTable({
) => void;
}) {
const { t } = useTranslation();
const { canEditCredentials } = React.useContext(ToolAccessContext);
const { canEditCredentials, canFixValues } =
React.useContext(ToolAccessContext);
const idPrefix = React.useId();
const [action, setAction] = React.useState<APIActionType>(apiAction);
@@ -1557,6 +1566,7 @@ function APIActionTable({
id={`${idPrefix}-${section}-${index}-filled-by-llm`}
aria-label={t('settings.tools.filledByLLM')}
checked={param.filled_by_llm}
disabled={!canFixValues}
onCheckedChange={(checked) =>
handlePropertyChange(
section,
@@ -1588,6 +1598,7 @@ function APIActionTable({
value={param.value}
disabled={
param.filled_by_llm ||
!canFixValues ||
(section === 'query_params' && !canEditCredentials)
}
onChange={(e) =>