diff --git a/backend/windmill-api-workspaces/src/workspaces.rs b/backend/windmill-api-workspaces/src/workspaces.rs index e886dd1a7c..6cb21f89b8 100644 --- a/backend/windmill-api-workspaces/src/workspaces.rs +++ b/backend/windmill-api-workspaces/src/workspaces.rs @@ -1656,7 +1656,6 @@ async fn create_pg_database( Path(w_id): Path, Json(req): Json, ) -> Result { - require_admin(authed.is_admin, &authed.username)?; windmill_common::validate_dbname(&req.target_dbname)?; // Non-superadmin: restrict dbname to wm_fork_ prefix @@ -1749,7 +1748,7 @@ struct ImportPgDatabaseRequest { fork_behavior: DataTableForkBehavior, } -/// Import (pg_dump/pg_import) from source to target. Does NOT create the target database. +/// Import (pg_dump/pg_import) from source to target async fn import_pg_database( authed: ApiAuthed, Extension(user_db): Extension, @@ -1776,6 +1775,15 @@ async fn import_pg_database( resolve_pg_source_checked(&db, &user_db, &authed, &w_id, &req.target).await?; if let Some(ref override_dbname) = req.target_dbname_override { + // Non-superadmin: restrict dbname to wm_fork_ prefix + if !windmill_common::auth::is_super_admin_email(&db, &authed.email).await? { + if !override_dbname.starts_with("wm_fork_") { + return Err(Error::BadRequest( + "Non-superadmin users can only override target dbname with names starting with 'wm_fork_'" + .to_string(), + )); + } + } target_pg.dbname = override_dbname.clone(); } @@ -1800,8 +1808,6 @@ async fn export_pg_schema( Path(w_id): Path, Json(req): Json, ) -> Result { - require_admin(authed.is_admin, &authed.username)?; - let pg = resolve_pg_source_checked(&db, &user_db, &authed, &w_id, &req.source).await?; let dump_file = pg_dump_database(&pg, true).await?; tokio::fs::read_to_string(&dump_file.path) @@ -1821,8 +1827,6 @@ async fn get_datatable_full_schema( Path(w_id): Path, Json(req): Json, ) -> JsonResult { - require_admin(authed.is_admin, &authed.username)?; - let pg = resolve_pg_source_checked(&db, &user_db, &authed, &w_id, &req.source).await?; let (client, connection) = pg.connect().await?; let join_handle = tokio::spawn(async move { connection.await }); diff --git a/backend/windmill-api-workspaces/src/workspaces_extra.rs b/backend/windmill-api-workspaces/src/workspaces_extra.rs index 27abf337a2..ba8a40dcee 100644 --- a/backend/windmill-api-workspaces/src/workspaces_extra.rs +++ b/backend/windmill-api-workspaces/src/workspaces_extra.rs @@ -865,8 +865,6 @@ pub async fn drop_forked_datatable_databases( Path(w_id): Path, Json(req): Json, ) -> Result>> { - require_admin(authed.is_admin, &authed.username)?; - let parent_w_id = sqlx::query_scalar!( "SELECT parent_workspace_id FROM workspace WHERE id = $1", &w_id @@ -932,6 +930,8 @@ pub async fn drop_forked_datatable_databases( continue; } }; + // We cannot drop the current database, so we connect to the parent's version to run DROP DATABASE on + // the forked version let parent_pg = match crate::workspaces::resolve_pg_source_checked( &db, &user_db, diff --git a/backend/windmill-common/src/workspaces.rs b/backend/windmill-common/src/workspaces.rs index 46573adb9f..4f1e1fbea2 100644 --- a/backend/windmill-common/src/workspaces.rs +++ b/backend/windmill-common/src/workspaces.rs @@ -562,7 +562,7 @@ pub async fn get_ducklake_from_db_unchecked( // This does not check for any permission. Should never be displayed to a user. #[async_recursion] -pub(crate) async fn transform_json_unchecked( +async fn transform_json_unchecked( value: &serde_json::Value, w_id: &str, db: &DB,