From 3531d29f6bb0beba84555b4d0d1389176048613e Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Thu, 30 Jan 2025 03:13:44 -0500 Subject: [PATCH] improve audit aprovals (#5161) * update audit log to track aproval and cancellation of job * approval to resource field * audit resource as json * refactor aproval audit logs * moving audit logs to worker_flow * striping u/ from auditor name --------- Co-authored-by: Ruben Fiszel --- backend/windmill-api/src/jobs.rs | 26 +++++++++++++----- backend/windmill-worker/src/worker_flow.rs | 31 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index f5a6c78df7..f039c220fb 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -1968,20 +1968,34 @@ async fn resume_suspended_job_internal( resume_immediately_if_relevant(parent_flow_info, job_id, &mut tx).await?; } + let approver = approver.unwrap_or_else(|| "anonymous".to_string()); + let audit_author = match authed { Some(authed) => (&authed).into(), - None => { - let approver = approver.unwrap_or_else(|| "anonymous".to_string()); - AuditAuthor { email: approver.clone(), username: approver, username_override: None } - } + None => AuditAuthor { + email: approver.clone(), + username: approver.clone(), + username_override: None, + }, }; audit_log( &mut *tx, &audit_author, - "jobs.approved", + "jobs.suspend_resume", ActionKind::Update, &w_id, - Some(&serde_json::json!({"approved": approved, "job_id": job_id}).to_string()), + Some( + &serde_json::json!({ + "approved": approved, + "job_id": job_id, + "details": if approved { + format!("Approved by {}", &approver) + } else { + format!("Cancelled by {}", &approver) + } + }) + .to_string(), + ), None, ) .await?; diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index b320b9a445..cf24258b9e 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -62,6 +62,8 @@ use windmill_queue::{ type DB = sqlx::Pool; +use windmill_audit::audit_ee::{audit_log, AuditAuthor}; +use windmill_audit::ActionKind; use windmill_queue::{canceled_job_to_result, push}; // #[instrument(level = "trace", skip_all)] @@ -1894,6 +1896,12 @@ async fn push_next_flow_job( )) && suspend.continue_on_disapprove_timeout.unwrap_or(false); + let audit_author = AuditAuthor { + username: flow_job.permissioned_as.trim_start_matches("u/").to_string(), + email: flow_job.email.clone(), + username_override: None, + }; + if can_be_resumed || disapproved_or_timeout_but_continue { if disapproved_or_timeout_but_continue { let js = if let Some(disapproved) = is_disapproved.as_ref() { @@ -1903,7 +1911,18 @@ async fn push_next_flow_job( }; resume_messages.push(to_raw_value(&js)); + audit_log( + &mut *tx, + &audit_author, + "jobs.suspend_resume", + ActionKind::Update, + &flow_job.workspace_id, + Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval but can continue".to_string()}).to_string()), + None, + ) + .await?; } + sqlx::query( "UPDATE queue SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2) @@ -1971,6 +1990,18 @@ async fn push_next_flow_job( /* cancelled or we're WaitingForEvents but we don't have enough messages (timed out) */ } else { + if is_disapproved.is_none() { + audit_log( + &mut *tx, + &audit_author, + "jobs.suspend_resume", + ActionKind::Update, + &flow_job.workspace_id, + Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval and is cancelled".to_string()}).to_string()), + None, + ) + .await?; + } tx.commit().await?; let (logs, error_name) = if let Some(disapprover) = is_disapproved {