From 2cde025d8e5b2d36c12c541d0597091764817549 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Wed, 2 Sep 2026 00:00:07 +0200 Subject: [PATCH] fix(datatables): guard the resource upsert too, and cache its query create_resource with update_if_exists is an edit when the row is already there, so it answered to none of the rule the other two write paths do. And the lookup the guard makes had no offline entry, which is what CI compiles against. --- ...23919cee3cc444725ac6b7906922554bae800.json | 23 +++++++++++++++++++ backend/windmill-store/src/resources.rs | 21 +++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 backend/.sqlx/query-63c16a4277983aaed0aed54972923919cee3cc444725ac6b7906922554bae800.json diff --git a/backend/.sqlx/query-63c16a4277983aaed0aed54972923919cee3cc444725ac6b7906922554bae800.json b/backend/.sqlx/query-63c16a4277983aaed0aed54972923919cee3cc444725ac6b7906922554bae800.json new file mode 100644 index 0000000000..4abea8d7e5 --- /dev/null +++ b/backend/.sqlx/query-63c16a4277983aaed0aed54972923919cee3cc444725ac6b7906922554bae800.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT value FROM resource WHERE path = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "value", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "63c16a4277983aaed0aed54972923919cee3cc444725ac6b7906922554bae800" +} diff --git a/backend/windmill-store/src/resources.rs b/backend/windmill-store/src/resources.rs index 9283c04c90..e71b03cda5 100644 --- a/backend/windmill-store/src/resources.rs +++ b/backend/windmill-store/src/resources.rs @@ -1187,6 +1187,27 @@ async fn create_resource( .await?; } if update_if_exists { + // An upsert over an existing row is an edit, so it answers to the same + // rule: a permissioned data table's roles live in the database its + // resource names, and that is not free to move while they exist. + let previous = sqlx::query_scalar!( + "SELECT value FROM resource WHERE path = $1 AND workspace_id = $2", + resource.path, + w_id + ) + .fetch_optional(&db) + .await? + .flatten(); + let nvalue: serde_json::Value = serde_json::from_str(raw_json.0.get()) + .map_err(|e| Error::BadRequest(format!("Invalid resource value: {e}")))?; + windmill_common::workspaces::ensure_resource_identity_change_allowed( + &db, + &w_id, + &resource.path, + previous.as_ref(), + Some(&nvalue), + ) + .await?; sqlx::query!( "INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at, labels)