Files
windmill/backend/windmill-audit/src/lib.rs
T
Jakub Kołodziejczak 72d0d8a7f5 chore(backend): upgrade sqlx to ^0.7 (#1865)
* DRAFT chore(backend): upgrade sqlx to ^0.7

related to:
* https://github.com/windmill-labs/windmill/pull/1858
* https://github.com/launchbadge/sqlx/issues/1163#issuecomment-1627685514

* (vol. 2) in 0.7, `Transaction` can no longer implement `Executor` directly

ref: https://github.com/launchbadge/sqlx/blob/afb6b1066e61f8e3875f530d96cfb5a299f13fda/examples/postgres/transaction/src/main.rs#L14-L17

notice that I'm temporarly using my custom patch
https://github.com/mrl5/sqlx/commit/16e4c9a8f380214ad61b1744d3e1f5fa866ffbcc
it's related to https://github.com/launchbadge/sqlx/issues/2611

* post git rebase chores

* use upstream fix from 0.7.1

* fix compile

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2023-07-15 14:06:20 +02:00

149 lines
4.1 KiB
Rust

/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2022
* This file and its contents are licensed under the AGPLv3 License.
* Please see the included NOTICE for copyright information and
* LICENSE-AGPL for a copy of the license.
*/
use sql_builder::prelude::*;
use std::collections::HashMap;
use windmill_common::{
error::{Error, Result},
utils::Pagination,
};
use serde::{Deserialize, Serialize};
use sql_builder::SqlBuilder;
use sqlx::{FromRow, Postgres, Transaction};
#[derive(sqlx::Type, Serialize, Deserialize, Debug)]
#[sqlx(type_name = "ACTION_KIND", rename_all = "lowercase")]
pub enum ActionKind {
Create,
Update,
Delete,
Execute,
}
#[derive(FromRow, Serialize, Deserialize)]
pub struct AuditLog {
pub workspace_id: String,
pub id: i32,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub username: String,
pub operation: String,
pub action_kind: ActionKind,
pub resource: Option<String>,
pub parameters: Option<serde_json::Value>,
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn audit_log<'c, E: sqlx::Executor<'c, Database = Postgres>>(
db: E,
username: &str,
operation: &str,
action_kind: ActionKind,
w_id: &str,
_resource: Option<&str>,
_parameters: Option<HashMap<&str, &str>>,
) -> Result<()> {
#[cfg(feature = "enterprise")]
let p_json: serde_json::Value = serde_json::to_value(&_parameters).unwrap();
#[cfg(not(feature = "enterprise"))]
let p_json: serde_json::Value = serde_json::json!({"redacted": "-"});
#[cfg(not(feature = "enterprise"))]
let _resource: Option<&str> = Some("EE only");
tracing::info!(
operation = operation,
action_kind = ?action_kind,
resource = _resource,
parameters = %p_json,
workspace_id = w_id,
username = username,
);
sqlx::query(
"INSERT INTO audit
(workspace_id, username, operation, action_kind, resource, parameters)
VALUES ($1, $2, $3, $4, $5, $6)",
)
.bind(w_id)
.bind(username)
.bind(operation)
.bind(action_kind)
.bind(_resource)
.bind(p_json)
.execute(db)
.await?;
Ok(())
}
#[derive(Deserialize)]
pub struct ListAuditLogQuery {
pub username: Option<String>,
pub operation: Option<String>,
pub action_kind: Option<String>,
pub resource: Option<String>,
pub before: Option<chrono::DateTime<chrono::Utc>>,
pub after: Option<chrono::DateTime<chrono::Utc>>,
}
pub async fn list_audit(
mut tx: Transaction<'_, sqlx::Postgres>,
w_id: String,
pagination: Pagination,
lq: ListAuditLogQuery,
) -> Result<Vec<AuditLog>> {
let (per_page, offset) = windmill_common::utils::paginate(pagination);
let mut sqlb = SqlBuilder::select_from("audit")
.field("*")
.order_by("id", true)
.and_where_eq("workspace_id", "?".bind(&w_id))
.offset(offset)
.limit(per_page)
.clone();
if let Some(u) = &lq.username {
sqlb.and_where_eq("username", "?".bind(u));
}
if let Some(o) = &lq.operation {
sqlb.and_where_eq("operation", "?".bind(o));
}
if let Some(ak) = &lq.action_kind {
sqlb.and_where_eq("action_kind", "?".bind(ak));
}
if let Some(r) = &lq.resource {
sqlb.and_where_eq("resource", "?".bind(r));
}
if let Some(b) = &lq.before {
sqlb.and_where_le("timestamp", format!("to_timestamp({})", b.timestamp()));
}
if let Some(a) = &lq.after {
sqlb.and_where_gt("timestamp", format!("to_timestamp({})", a.timestamp()));
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let rows = sqlx::query_as::<_, AuditLog>(&sql)
.fetch_all(&mut *tx)
.await?;
Ok(rows)
}
pub async fn get_audit(mut tx: Transaction<'_, sqlx::Postgres>, id: i32) -> Result<AuditLog> {
let audit_o = sqlx::query_as::<_, AuditLog>("SELECT * FROM audit WHERE id = $1")
.bind(id)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
let audit = windmill_common::utils::not_found_if_none(audit_o, "AuditLog", &id.to_string())?;
Ok(audit)
}