Make changing engine-run code superuser-only
Writing node source, installing modules and any flow save that adds, retypes or repoints a code node (Function, Command), edits a Command node's settings or changes a node's resources.env now answer 403 for everyone but a superuser. MCP tools and the assistant reach the same routes, so they inherit the gate. Node types report runs_code. The editor shows code read-only with a note, disables a Command node's settings, leaves code nodes and reusable nodes out of the palette and paste, and the Modules page offers no install.
This commit is contained in:
@@ -42,6 +42,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import useAuth from "@/hooks/useAuth"
|
||||
import useCustomToast from "@/hooks/useCustomToast"
|
||||
import { inCodeEditor, useShortcuts } from "@/lib/shortcuts"
|
||||
import { cn } from "@/lib/utils"
|
||||
@@ -1204,6 +1205,9 @@ const PLACEHOLDER: Record<DType, string> = {
|
||||
video: 'fluksio.save_artifact(b"", "clip.mp4", media_type="video/mp4")',
|
||||
}
|
||||
|
||||
/** Why a code editor or an exec node's settings are read-only. */
|
||||
const CODE_LOCKED = "Only a superuser can change code the engine runs."
|
||||
|
||||
const SCAFFOLD_DOC =
|
||||
'"""A new node. Return a dict keyed by your output ports."""'
|
||||
|
||||
@@ -1293,6 +1297,12 @@ function PanelBody({
|
||||
...nodeSourceQueryOptions(flow, node.id),
|
||||
enabled: hasSource,
|
||||
})
|
||||
// Code the engine runs is a superuser's to change; the server refuses
|
||||
// anyone else, so the panel shows it read-only rather than failing a save.
|
||||
const { user } = useAuth()
|
||||
const mayEditCode = Boolean(user?.is_superuser)
|
||||
const settingsAreCode =
|
||||
!mayEditCode && Boolean(nodeType?.runs_code) && !hasSource
|
||||
|
||||
const [code, setCode] = useState<string | null>(null)
|
||||
// The port the X is dropping or its name field renaming, noted just before
|
||||
@@ -1333,7 +1343,14 @@ function PanelBody({
|
||||
const editNode = (next: NodeDef_Input) => {
|
||||
onChange(next)
|
||||
const current = code ?? source?.code
|
||||
if (!hasSource || next.source_ref || origin || current === undefined) return
|
||||
if (
|
||||
!hasSource ||
|
||||
!mayEditCode ||
|
||||
next.source_ref ||
|
||||
origin ||
|
||||
current === undefined
|
||||
)
|
||||
return
|
||||
const wanted = scaffoldFor(next)
|
||||
if (current !== wanted && SCAFFOLD_SHAPE.test(current)) editCode(wanted)
|
||||
}
|
||||
@@ -1389,6 +1406,16 @@ function PanelBody({
|
||||
}
|
||||
}, [])
|
||||
|
||||
const paramsForm = (
|
||||
<ParamsForm
|
||||
type={node.type}
|
||||
schema={nodeType?.params_schema}
|
||||
params={node.params ?? {}}
|
||||
provides={node.provides ?? []}
|
||||
onChange={(params) => editNode({ ...node, params })}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -1434,13 +1461,20 @@ function PanelBody({
|
||||
portMove.current = [portName(spec), null]
|
||||
}}
|
||||
/>
|
||||
<ParamsForm
|
||||
type={node.type}
|
||||
schema={nodeType?.params_schema}
|
||||
params={node.params ?? {}}
|
||||
provides={node.provides ?? []}
|
||||
onChange={(params) => editNode({ ...node, params })}
|
||||
/>
|
||||
{settingsAreCode ? (
|
||||
/* An exec node's command is its code, so the whole form is. */
|
||||
<fieldset disabled className="grid min-w-0 gap-2">
|
||||
{paramsForm}
|
||||
<p
|
||||
className="text-xs text-muted-foreground"
|
||||
data-testid="node-settings-locked"
|
||||
>
|
||||
{CODE_LOCKED}
|
||||
</p>
|
||||
</fieldset>
|
||||
) : (
|
||||
paramsForm
|
||||
)}
|
||||
{nodeType?.free_params ? (
|
||||
<FreeParamsForm
|
||||
params={node.params ?? {}}
|
||||
@@ -1524,7 +1558,14 @@ function PanelBody({
|
||||
<span className="font-mono">{origin.repo || "a repository"}</span>
|
||||
. Edit it there and sync again.
|
||||
</p>
|
||||
) : null}
|
||||
) : mayEditCode ? null : (
|
||||
<p
|
||||
className="text-xs text-muted-foreground"
|
||||
data-testid="node-source-locked"
|
||||
>
|
||||
{CODE_LOCKED}
|
||||
</p>
|
||||
)}
|
||||
<div className="min-h-0 flex-1 overflow-hidden rounded-md border border-border">
|
||||
<Suspense
|
||||
fallback={
|
||||
@@ -1534,7 +1575,7 @@ function PanelBody({
|
||||
<NodeEditor
|
||||
value={code ?? source?.code ?? ""}
|
||||
onChange={editCode}
|
||||
readOnly={Boolean(origin)}
|
||||
readOnly={Boolean(origin) || !mayEditCode}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user