-
Notifications
You must be signed in to change notification settings - Fork 321
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5002 from systeminit/nick/eng-2854
Write audit logs to database in forklift
- Loading branch information
Showing
33 changed files
with
1,458 additions
and
985 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
//! Contains functionality for setting up and communicating with the audit database. | ||
use chrono::DateTime; | ||
use chrono::Utc; | ||
use si_data_pg::PgError; | ||
use si_data_pg::PgPoolError; | ||
use si_events::audit_log::AuditLogKind; | ||
use si_events::audit_log::AuditLogMetadata; | ||
use si_events::Actor; | ||
use si_events::ChangeSetId; | ||
use si_events::WorkspacePk; | ||
use telemetry::prelude::*; | ||
use thiserror::Error; | ||
|
||
mod config; | ||
mod context; | ||
mod migrate; | ||
|
||
pub use config::AuditDatabaseConfig; | ||
pub use config::DBNAME; | ||
pub use context::AuditDatabaseContext; | ||
pub use context::AuditDatabaseContextError; | ||
pub use migrate::{migrate, AuditDatabaseMigrationError}; | ||
|
||
#[allow(missing_docs)] | ||
#[derive(Error, Debug)] | ||
pub enum AuditDatabaseError { | ||
#[error("chrono parse error: {0}")] | ||
ChronoParse(#[from] chrono::ParseError), | ||
#[error("pg error: {0}")] | ||
Pg(#[from] PgError), | ||
#[error("pg pool error: {0}")] | ||
PgPool(#[from] PgPoolError), | ||
#[error("serde json error: {0}")] | ||
SerdeJson(#[from] serde_json::Error), | ||
} | ||
|
||
type Result<T> = std::result::Result<T, AuditDatabaseError>; | ||
|
||
#[allow(clippy::too_many_arguments, missing_docs)] | ||
#[instrument( | ||
name = "audit_log.insert", | ||
level = "debug", | ||
skip_all, | ||
fields( | ||
si.workspace.id = %workspace_id, | ||
), | ||
)] | ||
pub async fn insert( | ||
context: &AuditDatabaseContext, | ||
workspace_id: WorkspacePk, | ||
kind: AuditLogKind, | ||
timestamp: String, | ||
change_set_id: Option<ChangeSetId>, | ||
actor: Actor, | ||
entity_name: Option<String>, | ||
) -> Result<()> { | ||
let kind_as_string = kind.to_string(); | ||
let user_id = match actor { | ||
Actor::System => None, | ||
Actor::User(user_id) => Some(user_id), | ||
}; | ||
|
||
let metadata = AuditLogMetadata::from(kind); | ||
let (title, entity_type) = metadata.title_and_entity_type(); | ||
let serialized_metadata = serde_json::to_value(metadata)?; | ||
let timestamp: DateTime<Utc> = timestamp.parse()?; | ||
|
||
context | ||
.pg_pool() | ||
.get() | ||
.await? | ||
.query_one( | ||
"INSERT INTO audit_logs ( | ||
workspace_id, | ||
kind, | ||
timestamp, | ||
title, | ||
change_set_id, | ||
user_id, | ||
entity_name, | ||
entity_type, | ||
metadata | ||
) VALUES ( | ||
$1, | ||
$2, | ||
$3, | ||
$4, | ||
$5, | ||
$6, | ||
$7, | ||
$8, | ||
$9 | ||
) RETURNING *", | ||
&[ | ||
&workspace_id, | ||
&kind_as_string, | ||
×tamp, | ||
&title, | ||
&change_set_id.map(|id| id.to_string()), | ||
&user_id.map(|id| id.to_string()), | ||
&entity_name, | ||
&entity_type, | ||
&serialized_metadata, | ||
], | ||
) | ||
.await?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use serde::{Deserialize, Serialize}; | ||
use si_data_pg::PgPoolConfig; | ||
|
||
/// The name of the audit database. | ||
pub const DBNAME: &str = "si_audit"; | ||
const APPLICATION_NAME: &str = "si-audit"; | ||
|
||
/// The configuration used for communicating with and setting up the audit database. | ||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
pub struct AuditDatabaseConfig { | ||
/// The configuration for the PostgreSQL pool. | ||
/// | ||
/// _Note:_ this is called "pg" for ease of use with layered load configuration files. | ||
pub pg: PgPoolConfig, | ||
} | ||
|
||
impl Default for AuditDatabaseConfig { | ||
fn default() -> Self { | ||
Self { | ||
pg: PgPoolConfig { | ||
dbname: DBNAME.into(), | ||
application_name: APPLICATION_NAME.into(), | ||
..Default::default() | ||
}, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use si_data_pg::{PgPool, PgPoolError}; | ||
use telemetry::prelude::*; | ||
use thiserror::Error; | ||
|
||
use super::AuditDatabaseConfig; | ||
|
||
#[allow(missing_docs)] | ||
#[derive(Error, Debug)] | ||
pub enum AuditDatabaseContextError { | ||
#[error("pg pool error: {0}")] | ||
PgPool(#[from] PgPoolError), | ||
} | ||
|
||
type Result<T> = std::result::Result<T, AuditDatabaseContextError>; | ||
|
||
/// The context used for communicating with and setting up the audit database. | ||
#[derive(Debug, Clone)] | ||
pub struct AuditDatabaseContext { | ||
pg_pool: PgPool, | ||
} | ||
|
||
impl AuditDatabaseContext { | ||
/// Creates an [`AuditDatabaseContext`] from an [`AuditDatabaseConfig`]. | ||
#[instrument(level = "info", name = "audit.context.from_config", skip_all)] | ||
pub async fn from_config(config: &AuditDatabaseConfig) -> Result<Self> { | ||
Ok(Self { | ||
pg_pool: PgPool::new(&config.pg).await?, | ||
}) | ||
} | ||
|
||
/// Returns a reference to the [`PgPool`]. | ||
pub fn pg_pool(&self) -> &PgPool { | ||
&self.pg_pool | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.