diff --git a/backend/.sqlx/query-387aebd876ef240f9c7c99c977d0b66587f50a70c24f331aa5838a754d13ada9.json b/backend/.sqlx/query-4c12e63ddcf4e8cfe2227421db8869c4fb77e2fa3b98d6b054995875f3fd5a10.json similarity index 61% rename from backend/.sqlx/query-387aebd876ef240f9c7c99c977d0b66587f50a70c24f331aa5838a754d13ada9.json rename to backend/.sqlx/query-4c12e63ddcf4e8cfe2227421db8869c4fb77e2fa3b98d6b054995875f3fd5a10.json index d02146d4d7..b07b2ae6b5 100644 --- a/backend/.sqlx/query-387aebd876ef240f9c7c99c977d0b66587f50a70c24f331aa5838a754d13ada9.json +++ b/backend/.sqlx/query-4c12e63ddcf4e8cfe2227421db8869c4fb77e2fa3b98d6b054995875f3fd5a10.json @@ -1,10 +1,15 @@ { "db_name": "PostgreSQL", - "query": "SELECT script_path FROM native_trigger\n WHERE workspace_id = $1 AND service_name = $2 AND connection_path = $3", + "query": "SELECT external_id, script_path FROM native_trigger\n WHERE workspace_id = $1 AND service_name = $2 AND connection_path = $3", "describe": { "columns": [ { "ordinal": 0, + "name": "external_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, "name": "script_path", "type_info": "Varchar" } @@ -28,8 +33,9 @@ ] }, "nullable": [ + false, false ] }, - "hash": "387aebd876ef240f9c7c99c977d0b66587f50a70c24f331aa5838a754d13ada9" + "hash": "4c12e63ddcf4e8cfe2227421db8869c4fb77e2fa3b98d6b054995875f3fd5a10" } diff --git a/backend/.sqlx/query-1ecd50aa13f5d4babdfaaba6ff56940ac9e6d7a28a27e576b5dda4d7e709ad1a.json b/backend/.sqlx/query-c0a782d90433c27c97eeb49cf620e993320337f046e95efbd5bc21adf69e3d7d.json similarity index 84% rename from backend/.sqlx/query-1ecd50aa13f5d4babdfaaba6ff56940ac9e6d7a28a27e576b5dda4d7e709ad1a.json rename to backend/.sqlx/query-c0a782d90433c27c97eeb49cf620e993320337f046e95efbd5bc21adf69e3d7d.json index 031c041870..013720b6ea 100644 --- a/backend/.sqlx/query-1ecd50aa13f5d4babdfaaba6ff56940ac9e6d7a28a27e576b5dda4d7e709ad1a.json +++ b/backend/.sqlx/query-c0a782d90433c27c97eeb49cf620e993320337f046e95efbd5bc21adf69e3d7d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT external_id, connection_path FROM native_trigger\n WHERE workspace_id = $1 AND service_name = $2 AND ($3::text IS NULL OR connection_path = $3)", + "query": "SELECT external_id, connection_path FROM native_trigger\n WHERE workspace_id = $1 AND service_name = $2 AND ($3::text[] IS NULL OR external_id = ANY($3))", "describe": { "columns": [ { @@ -29,7 +29,7 @@ } } }, - "Text" + "TextArray" ] }, "nullable": [ @@ -37,5 +37,5 @@ true ] }, - "hash": "1ecd50aa13f5d4babdfaaba6ff56940ac9e6d7a28a27e576b5dda4d7e709ad1a" + "hash": "c0a782d90433c27c97eeb49cf620e993320337f046e95efbd5bc21adf69e3d7d" } diff --git a/backend/.sqlx/query-6e1231d0db1d3736b05a41aa0a3f125cb2f383a53645d7c48cbd0478c3364663.json b/backend/.sqlx/query-e4407cb73d41652088bb6dcef2503e91a190217cc7875ebb07928b25720ae20b.json similarity index 73% rename from backend/.sqlx/query-6e1231d0db1d3736b05a41aa0a3f125cb2f383a53645d7c48cbd0478c3364663.json rename to backend/.sqlx/query-e4407cb73d41652088bb6dcef2503e91a190217cc7875ebb07928b25720ae20b.json index 84fb956583..def86ca96f 100644 --- a/backend/.sqlx/query-6e1231d0db1d3736b05a41aa0a3f125cb2f383a53645d7c48cbd0478c3364663.json +++ b/backend/.sqlx/query-e4407cb73d41652088bb6dcef2503e91a190217cc7875ebb07928b25720ae20b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM native_trigger\n WHERE workspace_id = $1 AND service_name = $2 AND ($3::text IS NULL OR connection_path = $3)\n RETURNING webhook_token_hash", + "query": "DELETE FROM native_trigger\n WHERE workspace_id = $1 AND service_name = $2 AND ($3::text[] IS NULL OR external_id = ANY($3))\n RETURNING webhook_token_hash", "describe": { "columns": [ { @@ -24,12 +24,12 @@ } } }, - "Text" + "TextArray" ] }, "nullable": [ false ] }, - "hash": "6e1231d0db1d3736b05a41aa0a3f125cb2f383a53645d7c48cbd0478c3364663" + "hash": "e4407cb73d41652088bb6dcef2503e91a190217cc7875ebb07928b25720ae20b" } diff --git a/backend/windmill-native-triggers/src/workspace_integrations.rs b/backend/windmill-native-triggers/src/workspace_integrations.rs index 49647a3545..ff2dee431f 100644 --- a/backend/windmill-native-triggers/src/workspace_integrations.rs +++ b/backend/windmill-native-triggers/src/workspace_integrations.rs @@ -348,22 +348,22 @@ async fn fetch_nextcloud_user_id(base_url: &str, access_token: &str) -> anyhow:: Ok(ocs.ocs.data.id) } -/// Delete the native triggers of a workspace+service — only those using `connection_path` when -/// given — including remote webhook cleanup. +/// Delete the native triggers of a workspace+service — only those in `external_ids` when given — +/// including remote webhook cleanup. /// This is best-effort: errors during remote cleanup or token deletion are logged but ignored. #[cfg(feature = "native_trigger")] async fn delete_triggers_for_service( db: &DB, workspace_id: &str, service_name: ServiceName, - connection_path: Option<&str>, + external_ids: Option<&[String]>, ) { let triggers = sqlx::query!( "SELECT external_id, connection_path FROM native_trigger - WHERE workspace_id = $1 AND service_name = $2 AND ($3::text IS NULL OR connection_path = $3)", + WHERE workspace_id = $1 AND service_name = $2 AND ($3::text[] IS NULL OR external_id = ANY($3))", workspace_id, service_name as ServiceName, - connection_path, + external_ids, ) .fetch_all(db) .await; @@ -423,11 +423,11 @@ async fn delete_triggers_for_service( // disconnected integration keep starting jobs. let deleted_token_hashes = sqlx::query_scalar!( "DELETE FROM native_trigger - WHERE workspace_id = $1 AND service_name = $2 AND ($3::text IS NULL OR connection_path = $3) + WHERE workspace_id = $1 AND service_name = $2 AND ($3::text[] IS NULL OR external_id = ANY($3)) RETURNING webhook_token_hash", workspace_id, service_name as ServiceName, - connection_path, + external_ids, ) .fetch_all(db) .await @@ -1084,9 +1084,10 @@ async fn delete_connection( ))); } - // Disconnecting deletes these triggers, so a path-scoped token must cover each of them. - let trigger_paths = sqlx::query_scalar!( - "SELECT script_path FROM native_trigger + // Disconnecting deletes these triggers, so a path-scoped token must cover each of them. Only + // the rows checked here are deleted: one created meanwhile is never removed unchecked. + let triggers = sqlx::query!( + "SELECT external_id, script_path FROM native_trigger WHERE workspace_id = $1 AND service_name = $2 AND connection_path = $3", workspace_id, service_name as ServiceName, @@ -1094,12 +1095,15 @@ async fn delete_connection( ) .fetch_all(&db) .await?; - for script_path in &trigger_paths { - check_scopes(&authed, || format!("native_triggers:write:{script_path}"))?; + for trigger in &triggers { + check_scopes(&authed, || { + format!("native_triggers:write:{}", trigger.script_path) + })?; } + let external_ids: Vec = triggers.into_iter().map(|t| t.external_id).collect(); // Before the connection goes: removing the registrations needs its token. - delete_triggers_for_service(&db, &workspace_id, service_name, Some(&path)).await; + delete_triggers_for_service(&db, &workspace_id, service_name, Some(&external_ids)).await; let mut tx = user_db.begin(&authed).await?; let account = cleanup_connection(&mut *tx, &workspace_id, &path).await?;