mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
fix: cap devops role at workspace admin and reject reserved on_behalf_of identities
Extends the job-token cap with three pieces: - `require_devops_role` takes `&ApiAuthed` and rejects job tokens. `is_devops_email` is true for superadmin emails, so every worker-management, instance-config and service-log route was reachable by the same superadmin `WM_TOKEN` that `require_super_admin` already rejects. - A `job_id` claim that does not parse as a uuid rejects the token rather than resolving to `None`, which would clear the job provenance and uncap it. Applies to the internal JWT and the external `jwt_ext_` path. - Defense in depth at store time: `validate_on_behalf_of` refuses the reserved internal sentinels as an `on_behalf_of` on apps/flows/scripts/schedules/triggers, and app execution refuses a policy carrying one — covering already-persisted and forked-app rows that predate the cap. Deploying on behalf of a real user, including a real superadmin, stays allowed; the cap handles that at execution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0b62ad31c0
commit
01ddbff6f6
@@ -591,6 +591,19 @@ async fn create_flow(
|
||||
}
|
||||
}
|
||||
|
||||
// Reject a forged superadmin run identity: only a preserved value is
|
||||
// caller-controlled (otherwise `resolve_on_behalf_of_email` stores the
|
||||
// deployer's own email below). A flow stores no permissioned_as, so this is
|
||||
// the sentinel guard.
|
||||
if nf.preserve_on_behalf_of.unwrap_or(false)
|
||||
&& windmill_common::can_preserve_on_behalf_of(&authed)
|
||||
{
|
||||
windmill_common::auth::validate_on_behalf_of(
|
||||
None,
|
||||
nf.on_behalf_of_email.as_deref(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
check_path_conflict(&mut tx, &w_id, &nf.path).await?;
|
||||
@@ -1031,6 +1044,17 @@ async fn update_flow(
|
||||
|
||||
validate_flow(&nf).await?;
|
||||
|
||||
// Reject a forged superadmin run identity in a preserved value (otherwise
|
||||
// `resolve_on_behalf_of_email` stores the deployer's own email below).
|
||||
if nf.preserve_on_behalf_of.unwrap_or(false)
|
||||
&& windmill_common::can_preserve_on_behalf_of(&authed)
|
||||
{
|
||||
windmill_common::auth::validate_on_behalf_of(
|
||||
None,
|
||||
nf.on_behalf_of_email.as_deref(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let authed = maybe_refresh_folders(&flow_path, &w_id, authed, &db).await;
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user