diff --git a/src/audit.rs b/src/audit.rs index 1622ad3e4984a9ff217f35333be640c54af142f5..5d9019fb9fc565e004153554c82efdcf6ac61958 100644 --- a/src/audit.rs +++ b/src/audit.rs @@ -270,12 +270,49 @@ pub fn root() -> Option<&'static slog::Logger> { ROOT.get() } -// TODO: enforce entry format by introducing new macro arguments (e.g. `severity:`). +::tarantool::define_str_enum! { + /// Type-safe entry severity for use in [`crate::audit!`]. + /// Severity levels and their usage are defined in the RFC. + pub enum Severity { + Low = "low", + Medium = "medium", + High = "high", + } +} + +/// This is the main API for adding new entries to the audit log. +/// The required fields are `message`, `title` and `severity`, +/// the rest is up to the caller. +/// +/// Example: +/// ``` +/// # use picodata::audit; +/// audit!( +/// message: "hello, world!", +/// title: "greeting", +/// severity: Low, +/// ); +/// ``` #[macro_export] macro_rules! audit( - ($($args:tt)+) => { + ( + message: $message:expr, + title: $title:expr, + severity: $severity:ident, + $($key:ident: $value:expr),* $(,)? + ) => { if let Some(root) = $crate::audit::root() { - slog::slog_log!(root, slog::Level::Info, "", $($args)*); + slog::slog_log!( + // Boilerplate required by slog. + root, slog::Level::Info, "", + // The message itself. + $message; + // Mandatory fields. + "title" => $title, + "severity" => $crate::audit::Severity::$severity.as_str(), + // Arbitrary fields. + $(stringify!($key) => $value,)* + ); } }; ); @@ -291,23 +328,23 @@ pub fn init(config: &str) { .expect("failed to initialize global audit drain"); crate::audit!( - "audit log is ready"; - "title" => "init_audit", - "severity" => "low", + message: "audit log is ready", + title: "init_audit", + severity: Low, ); // Report a local startup event & register a trigger for a local shutdown event. // Those will only be seen in this exact instance's audit log (hence "local"). crate::audit!( - "instance is starting"; - "title" => "local_startup", - "severity" => "low", + message: "instance is starting", + title: "local_startup", + severity: Low, ); ::tarantool::trigger::on_shutdown(|| { crate::audit!( - "instance is shutting down"; - "title" => "local_shutdown", - "severity" => "high", + message: "instance is shutting down", + title: "local_shutdown", + severity: High, ); }) .expect("failed to install audit trigger for instance shutdown"); diff --git a/src/lib.rs b/src/lib.rs index 18913844fed9160ceb104cb1cc5d725796c34f02..c397ac3bd69a3a1c65c3777e14803d3e4846e121 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,7 @@ use crate::tier::{Tier, DEFAULT_TIER}; use crate::traft::op; use crate::util::{unwrap_or_terminate, validate_and_complete_unix_socket_path}; -mod audit; +pub mod audit; mod bootstrap_entries; pub mod cas; pub mod cli; diff --git a/src/storage.rs b/src/storage.rs index 14b975d007ddeac9b8c49d453dea3b140c2a3ae2..1e9c57e248f7db26c0e93532aa5377b399aacbd0 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -2665,10 +2665,10 @@ pub mod acl { let user = &user_def.name; crate::audit!( - "created user `{user}`"; - "title" => "create_user", - "severity" => "high", - "auth_type" => user_def.auth.method.as_str(), + message: "created user `{user}`", + title: "create_user", + severity: High, + auth_type: user_def.auth.method.as_str(), ); Ok(()) @@ -2685,10 +2685,10 @@ pub mod acl { let user_def = storage.users.by_id(user_id)?.expect("failed to get user"); let user = &user_def.name; crate::audit!( - "password of user `{user}` was changed"; - "title" => "change_password", - "severity" => "high", - "auth_type" => auth.method.as_str(), + message: "password of user `{user}` was changed", + title: "change_password", + severity: High, + auth_type: auth.method.as_str(), ); Ok(()) @@ -2703,9 +2703,9 @@ pub mod acl { let user = &user_def.name; crate::audit!( - "dropped user `{user}`"; - "severity" => "medium", - "title" => "drop_user", + message: "dropped user `{user}`", + title: "drop_user", + severity: Medium, ); Ok(()) @@ -2717,9 +2717,9 @@ pub mod acl { let role = &role_def.name; crate::audit!( - "created role `{role}`"; - "title" => "create_role", - "severity" => "high", + message: "created role `{role}`", + title: "create_role", + severity: High, ); Ok(()) @@ -2739,9 +2739,9 @@ pub mod acl { let role = &role_def.name; crate::audit!( - "dropped role `{role}`"; - "title" => "drop_role", - "severity" => "medium", + message: "dropped role `{role}`", + title: "drop_role", + severity: Medium, ); } @@ -2762,17 +2762,17 @@ pub mod acl { match (privilege.as_str(), object_type.as_str()) { ("execute", "role") => { crate::audit!( - "granted role `{object}` to {grantee_type} `{grantee}`"; - "title" => "grant_role", - "severity" => "high", + message: "granted role `{object}` to {grantee_type} `{grantee}`", + title: "grant_role", + severity: High, ); } _ => { crate::audit!( - "granted privilege {privilege} on {object_type} `{object}` \ - to {grantee_type} `{grantee}`"; - "title" => "grant_privilege", - "severity" => "high", + message: "granted privilege {privilege} on {object_type} `{object}` \ + to {grantee_type} `{grantee}`", + title: "grant_privilege", + severity: High, ); } } @@ -2800,17 +2800,17 @@ pub mod acl { match (privilege.as_str(), object_type.as_str()) { ("execute", "role") => { crate::audit!( - "revoke role `{object}` from {grantee_type} `{grantee}`"; - "title" => "revoke_role", - "severity" => "high", + message: "revoke role `{object}` from {grantee_type} `{grantee}`", + title: "revoke_role", + severity: High, ); } _ => { crate::audit!( - "revoked privilege {privilege} on {object_type} `{object}` \ - from {grantee_type} `{grantee}`"; - "title" => "revoke_privilege", - "severity" => "high", + message: "revoked privilege {privilege} on {object_type} `{object}` \ + from {grantee_type} `{grantee}`", + title: "revoke_privilege", + severity: High, ); } } diff --git a/src/traft/node.rs b/src/traft/node.rs index 8dd0f37243e8da58cf94f7329f9ffde5e3a1ce17..dff31a071b11e381b4e516dedd82985ffbbf301e 100644 --- a/src/traft/node.rs +++ b/src/traft/node.rs @@ -680,9 +680,9 @@ impl NodeImpl { if prev.as_ref().map(|x| x.raft_id) != Some(new.raft_id) { let instance_id = &new.instance_id; crate::audit!( - "added a new instance `{instance_id}` to the cluster"; - "title" => "create_database", - "severity" => "low", + message: "added a new instance `{instance_id}` to the cluster", + title: "create_database", + severity: Low, ); } @@ -690,9 +690,9 @@ impl NodeImpl { let instance_id = &new.instance_id; let grade = &new.current_grade; crate::audit!( - "current grade of instance `{instance_id}` changed to {grade}"; - "title" => "change_current_grade", - "severity" => "medium", + message: "current grade of instance `{instance_id}` changed to {grade}", + title: "change_current_grade", + severity: Medium, ); } @@ -700,9 +700,9 @@ impl NodeImpl { let instance_id = &new.instance_id; let grade = &new.target_grade; crate::audit!( - "target grade of instance `{instance_id}` changed to {grade}"; - "title" => "change_target_grade", - "severity" => "low", + message: "target grade of instance `{instance_id}` changed to {grade}", + title: "change_target_grade", + severity: Low, ); } @@ -787,9 +787,9 @@ impl NodeImpl { .expect("storage shouldn't fail"); crate::audit!( - "created table `{name}`"; - "title" => "create_table", - "severity" => "medium", + message: "created table `{name}`", + title: "create_table", + severity: Medium, ); } @@ -800,9 +800,9 @@ impl NodeImpl { let name = &space.name; crate::audit!( - "dropped table `{name}`"; - "title" => "drop_table", - "severity" => "medium", + message: "dropped table `{name}`", + title: "drop_table", + severity: Medium, ); }