mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-04 08:02:23 +00:00
feat: restricted job tokens per script and flow (#11484)
* feat: restricted job tokens (job_token_scopes on scripts and flows) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: admit flow-run reads, skip dedicated workers, gate on worker version Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep restricted jobs off flow runners, preserve scopes on rename and promotion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep restricted jobs off every dedicated handoff, confine progress flow id Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: exclude restricted runnables from dedicated worker startup Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: gate restrictions on the release after 1.821.0 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: store per-job scopes on job_perms instead of v2_job, pin inline runs to the checked version Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: step-level job_token_scopes for flow steps and agent tools Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: fail closed on perms read errors, refuse restricted queue imports, gate step scopes in previews Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: carry a job's scopes on its completion so a re-run keeps the caller's cap Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: carry a zombie job's scopes into its completion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: leave a zombie for the next sweep when its scopes cannot be read Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * docs: correct the QueuedJobV2 completion comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: validate step scopes in batch flows, fail closed on unvalidated step scopes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: refuse flows with step or tool restrictions at push while an older worker is live Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: apply the step-scope worker gate to flow restarts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: list the job token toggle with the other step and flow settings Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * chore: pin the EE companion merged with EE main Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * perf: skip scope lookups for unrestricted jobs; list job token setting last Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * style: rustfmt scopes tests Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: confine restricted job tokens to their own run lineage; drop remaining extra lookups Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * chore: update ee-repo-ref to 259ad3bfeef5285ba80eedc86309b11dca001220 This commit updates the EE repository reference after PR #843 was merged in windmill-ee-private. Previous ee-repo-ref: 2b77c0225dca441235daf7bf0a06ba968df0c927 New ee-repo-ref: 259ad3bfeef5285ba80eedc86309b11dca001220 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
windmill-internal-app[bot]
parent
e952298f84
commit
e7fc1b2e2e
@@ -52,6 +52,7 @@ export interface FlowFile {
|
||||
schema?: any;
|
||||
on_behalf_of_email?: string;
|
||||
has_on_behalf_of?: boolean;
|
||||
job_token_scopes?: string[] | null;
|
||||
// Mirrors granular ACLs on the flow path. Omitted from flow.yaml when no
|
||||
// perms are set. The CLI applies diffs through /acls/add and /acls/remove
|
||||
// (see applyExtraPermsDiff) — never through update_flow — so a perm-only
|
||||
@@ -273,12 +274,22 @@ export async function pushFlow(
|
||||
// so a perm-only edit never bumps the flow version. Strip the field from the
|
||||
// body that goes to update_flow / create_flow and treat it as a separate
|
||||
// step both for the up-to-date short-circuit and after the deploy.
|
||||
const { extra_perms: localPerms, ...localFlowBody } = localFlow as FlowFile & {
|
||||
const {
|
||||
extra_perms: localPerms,
|
||||
job_token_scopes: localJobTokenScopes,
|
||||
...localFlowBody
|
||||
} = localFlow as FlowFile & {
|
||||
extra_perms?: Record<string, boolean>;
|
||||
};
|
||||
// Always sent, unlike the other settings: the server keeps a restriction the body omits,
|
||||
// so a flow.yaml without the key has to clear it explicitly.
|
||||
const jobTokenScopes = localJobTokenScopes ?? null;
|
||||
|
||||
if (flow) {
|
||||
if (isSuperset(localFlowBody, flow)) {
|
||||
if (
|
||||
isSuperset(localFlowBody, flow) &&
|
||||
JSON.stringify(jobTokenScopes) === JSON.stringify(flow.job_token_scopes ?? null)
|
||||
) {
|
||||
log.info(colors.green(`Flow ${remotePath} is up to date`));
|
||||
} else {
|
||||
log.info(colors.bold.yellow(`Updating flow ${remotePath}...`));
|
||||
@@ -289,6 +300,7 @@ export async function pushFlow(
|
||||
path: remotePath.replaceAll(SEP, "/"),
|
||||
deployment_message: message,
|
||||
...localFlowBody,
|
||||
job_token_scopes: jobTokenScopes,
|
||||
...preserveFields,
|
||||
// Preserve any user draft at this path (see backend skip_draft_deletion).
|
||||
skip_draft_deletion: true,
|
||||
@@ -304,6 +316,7 @@ export async function pushFlow(
|
||||
path: remotePath.replaceAll(SEP, "/"),
|
||||
deployment_message: message,
|
||||
...localFlowBody,
|
||||
job_token_scopes: jobTokenScopes,
|
||||
...preserveFields,
|
||||
// Preserve any user draft at this path (see backend skip_draft_deletion).
|
||||
skip_draft_deletion: true,
|
||||
|
||||
@@ -636,6 +636,9 @@ export async function handleFile(
|
||||
envs: typed?.envs,
|
||||
modules: modules,
|
||||
labels: typed?.labels,
|
||||
// Always sent: the server keeps a restriction the body omits, so a script.yaml without
|
||||
// the key has to clear it explicitly.
|
||||
job_token_scopes: typed?.job_token_scopes ?? null,
|
||||
};
|
||||
|
||||
const hasOnBehalfOf = (typed as any)?.has_on_behalf_of ?? !!typed?.on_behalf_of_email;
|
||||
@@ -712,6 +715,7 @@ export async function handleFile(
|
||||
(hasOnBehalfOf ? true : typed.on_behalf_of_email == remote.on_behalf_of_email) &&
|
||||
deepEqual(typed.envs, remote.envs) &&
|
||||
deepEqual(typed.labels ?? null, remote.labels ?? null) &&
|
||||
deepEqual(typed.job_token_scopes ?? null, remote.job_token_scopes ?? null) &&
|
||||
deepEqual(modules ?? null, remote.modules ?? null))
|
||||
) {
|
||||
log.info(colors.green(`Script ${remotePath} is up to date`));
|
||||
|
||||
Generated
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user