From f4536be60a5f5c86113c7ffa7c70061b6c2f519b Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 13 Jun 2025 10:25:27 +0200 Subject: [PATCH 01/23] VPN crud events --- .../src/db/models/audit_log/metadata.rs | 5 ++++ crates/defguard_core/src/events.rs | 9 +++++++ .../defguard_core/src/handlers/wireguard.rs | 22 +++++++++++++++- crates/defguard_event_logger/src/lib.rs | 25 ++++++++++--------- crates/defguard_event_logger/src/message.rs | 9 +++---- .../defguard_event_router/src/handlers/api.rs | 9 +++++++ web/src/i18n/en/index.ts | 3 +++ web/src/i18n/i18n-types.ts | 24 ++++++++++++++++++ web/src/pages/activity/types.ts | 8 +++++- 9 files changed, 94 insertions(+), 20 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 10cfe8668..35281d171 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -94,3 +94,8 @@ pub struct VpnClientMfaMetadata { pub struct EnrollmentDeviceAddedMetadata { pub device: Device, } + +#[derive(Serialize)] +pub struct VpnLocationMetadata { + pub location: WireguardNetwork, +} diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index d73d95e78..01d1a2ac7 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -142,6 +142,15 @@ pub enum ApiEventType { stream_id: Id, stream_name: String, }, + VpnLocationAdded { + location: WireguardNetwork, + }, + VpnLocationRemoved { + location: WireguardNetwork, + }, + VpnLocationModified { + location: WireguardNetwork, + }, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index 761008376..d686f9d65 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -129,6 +129,7 @@ pub(crate) async fn create_network( _role: AdminRole, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Json(data): Json, ) -> ApiResult { let network_name = data.name.clone(); @@ -170,6 +171,13 @@ pub(crate) async fn create_network( "User {} created WireGuard network {network_name}", session.user.username ); + + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::VpnLocationAdded { + location: network.clone(), + }, + })?; update_counts(&appstate.pool).await?; Ok(ApiResponse { @@ -205,6 +213,7 @@ pub(crate) async fn modify_network( Path(network_id): Path, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Json(data): Json, ) -> ApiResult { debug!( @@ -250,6 +259,12 @@ pub(crate) async fn modify_network( "User {} updated WireGuard network {network_id}", session.user.username, ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::VpnLocationModified { + location: network.clone(), + }, + })?; Ok(ApiResponse { json: json!(network), status: StatusCode::OK, @@ -276,6 +291,7 @@ pub(crate) async fn delete_network( Path(network_id): Path, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, ) -> ApiResult { debug!( "User {} deleting WireGuard network {network_id}", @@ -290,13 +306,17 @@ pub(crate) async fn delete_network( for device in network_devices { device.delete(&mut *transaction).await?; } - network.delete(&mut *transaction).await?; + network.clone().delete(&mut *transaction).await?; transaction.commit().await?; appstate.send_wireguard_event(GatewayEvent::NetworkDeleted(network_id, network_name)); info!( "User {} deleted WireGuard network {network_id}", session.user.username, ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::VpnLocationRemoved { location: network }, + })?; update_counts(&appstate.pool).await?; Ok(ApiResponse::default()) diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 26b4d7ff3..0477e0995 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -15,6 +15,7 @@ use defguard_core::db::{ MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, + VpnLocationMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -220,18 +221,18 @@ pub async fn run_event_logger( }) .ok(), ), - DefguardEvent::VpnLocationAdded { - location_id: _, - location_name: _, - } => todo!(), - DefguardEvent::VpnLocationRemoved { - location_id: _, - location_name: _, - } => todo!(), - DefguardEvent::VpnLocationModified { - location_id: _, - location_name: _, - } => todo!(), + DefguardEvent::VpnLocationAdded { location } => ( + EventType::VpnLocationAdded, + serde_json::to_value(VpnLocationMetadata { location }).ok(), + ), + DefguardEvent::VpnLocationRemoved { location } => ( + EventType::VpnLocationRemoved, + serde_json::to_value(VpnLocationMetadata { location }).ok(), + ), + DefguardEvent::VpnLocationModified { location } => ( + EventType::VpnLocationModified, + serde_json::to_value(VpnLocationMetadata { location }).ok(), + ), DefguardEvent::OpenIdAppAdded { app_id: _, app_name: _, diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 06d2b178a..845537e08 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -193,16 +193,13 @@ pub enum DefguardEvent { }, // VPN location management VpnLocationAdded { - location_id: Id, - location_name: String, + location: WireguardNetwork, }, VpnLocationRemoved { - location_id: Id, - location_name: String, + location: WireguardNetwork, }, VpnLocationModified { - location_id: Id, - location_name: String, + location: WireguardNetwork, }, // OpenID app management OpenIdAppAdded { diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 4c4c9dd4e..9040c6ac4 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -135,6 +135,15 @@ impl EventRouter { stream_name, }) } + ApiEventType::VpnLocationAdded { location } => { + LoggerEvent::Defguard(DefguardEvent::VpnLocationAdded { location }) + } + ApiEventType::VpnLocationRemoved { location } => { + LoggerEvent::Defguard(DefguardEvent::VpnLocationRemoved { location }) + } + ApiEventType::VpnLocationModified { location } => { + LoggerEvent::Defguard(DefguardEvent::VpnLocationModified { location }) + } }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index c2df2e3af..4d7bb0e0f 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2581,6 +2581,9 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T password_reset_requested: 'Password reset requested', password_reset_started: 'Password reset started', password_reset_completed: 'Password reset completed', + vpn_location_added: 'VPN location added', + vpn_location_removed: 'VPN location removed', + vpn_location_modified: 'VPN location modified', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 8aadfa84a..9582ad9d6 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6238,6 +6238,18 @@ type RootTranslation = { * P​a​s​s​w​o​r​d​ ​r​e​s​e​t​ ​c​o​m​p​l​e​t​e​d */ password_reset_completed: string + /** + * V​P​N​ ​l​o​c​a​t​i​o​n​ ​a​d​d​e​d + */ + vpn_location_added: string + /** + * V​P​N​ ​l​o​c​a​t​i​o​n​ ​r​e​m​o​v​e​d + */ + vpn_location_removed: string + /** + * V​P​N​ ​l​o​c​a​t​i​o​n​ ​m​o​d​i​f​i​e​d + */ + vpn_location_modified: string } auditModule: { /** @@ -12423,6 +12435,18 @@ export type TranslationFunctions = { * Password reset completed */ password_reset_completed: () => LocalizedString + /** + * VPN location added + */ + vpn_location_added: () => LocalizedString + /** + * VPN location removed + */ + vpn_location_removed: () => LocalizedString + /** + * VPN location modified + */ + vpn_location_modified: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 212873765..9b29c112a 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -43,7 +43,10 @@ export type AuditEventType = | 'enrollment_completed' | 'password_reset_requested' | 'password_reset_started' - | 'password_reset_completed'; + | 'password_reset_completed' + | 'vpn_location_added' + | 'vpn_location_removed' + | 'vpn_location_modified'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -82,4 +85,7 @@ export const auditEventTypeValues: AuditEventType[] = [ 'password_reset_requested', 'password_reset_started', 'password_reset_completed', + 'vpn_location_added', + 'vpn_location_removed', + 'vpn_location_modified', ]; From edf3a566461e4e2ebabe46de0aa7db4e3b28c6a2 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 13 Jun 2025 10:53:51 +0200 Subject: [PATCH 02/23] Emit VpnLocationAdded event when importing from file --- crates/defguard_core/src/handlers/wireguard.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index d686f9d65..04076fee0 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -487,6 +487,7 @@ pub(crate) async fn remove_gateway( pub(crate) async fn import_network( _role: AdminRole, State(appstate): State, + context: ApiRequestContext, Json(data): Json, ) -> ApiResult { debug!("Importing network from config file"); @@ -527,7 +528,12 @@ pub(crate) async fn import_network( transaction.commit().await?; info!("Imported network {network} with {} devices", devices.len()); - + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::VpnLocationAdded { + location: network.clone(), + }, + })?; update_counts(&appstate.pool).await?; Ok(ApiResponse { From 28ee761b43f617132d2f328942037045bd2025bd Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 13 Jun 2025 12:23:04 +0200 Subject: [PATCH 03/23] ApiToken event handling --- .../src/db/models/audit_log/metadata.rs | 15 +++++- .../src/db/models/audit_log/mod.rs | 4 ++ .../src/enterprise/db/models/api_tokens.rs | 2 +- .../src/enterprise/handlers/api_tokens.rs | 49 +++++++++++++++++-- crates/defguard_core/src/events.rs | 15 +++++- crates/defguard_event_logger/src/lib.rs | 43 +++++++++------- crates/defguard_event_logger/src/message.rs | 12 +++-- .../defguard_event_router/src/handlers/api.rs | 15 ++++++ web/src/i18n/en/index.ts | 3 ++ web/src/i18n/i18n-types.ts | 24 +++++++++ web/src/pages/activity/types.ts | 8 ++- 11 files changed, 161 insertions(+), 29 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 35281d171..9a21ae880 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -1,4 +1,4 @@ -use crate::db::{Device, Id, MFAMethod, WireguardNetwork}; +use crate::db::{Device, Id, MFAMethod, User, WireguardNetwork}; #[derive(Serialize)] pub struct MfaLoginMetadata { @@ -99,3 +99,16 @@ pub struct EnrollmentDeviceAddedMetadata { pub struct VpnLocationMetadata { pub location: WireguardNetwork, } + +#[derive(Serialize)] +pub struct ApiTokenMetadata { + pub owner: User, + pub token_name: String, +} + +#[derive(Serialize)] +pub struct ApiTokenRenamedMetadata { + pub owner: User, + pub old_name: String, + pub new_name: String, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index 0e4d6f24a..71f3b9670 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -75,6 +75,10 @@ pub enum EventType { PasswordResetRequested, PasswordResetStarted, PasswordResetCompleted, + // API token management, + ApiTokenAdded, + ApiTokenRemoved, + ApiTokenRenamed, } #[derive(Model, FromRow, Serialize)] diff --git a/crates/defguard_core/src/enterprise/db/models/api_tokens.rs b/crates/defguard_core/src/enterprise/db/models/api_tokens.rs index 8bb6d879b..62bd31375 100644 --- a/crates/defguard_core/src/enterprise/db/models/api_tokens.rs +++ b/crates/defguard_core/src/enterprise/db/models/api_tokens.rs @@ -4,7 +4,7 @@ use sqlx::{query_as, Error as SqlxError, PgExecutor}; use crate::db::{Id, NoId}; -#[derive(Deserialize, Model, Serialize)] +#[derive(Clone, Deserialize, Model, Serialize)] #[table(api_token)] pub struct ApiToken { id: I, diff --git a/crates/defguard_core/src/enterprise/handlers/api_tokens.rs b/crates/defguard_core/src/enterprise/handlers/api_tokens.rs index 2f11c46ad..ad9ac1e2a 100644 --- a/crates/defguard_core/src/enterprise/handlers/api_tokens.rs +++ b/crates/defguard_core/src/enterprise/handlers/api_tokens.rs @@ -10,8 +10,10 @@ use super::LicenseInfo; use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, + db::User, enterprise::db::models::api_tokens::{ApiToken, ApiTokenInfo}, error::WebError, + events::{ApiEvent, ApiEventType, ApiRequestContext}, handlers::{user_for_admin_or_self, ApiResponse, ApiResult}, random::gen_alphanumeric, }; @@ -28,6 +30,7 @@ pub async fn add_api_token( _admin: AdminRole, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Path(username): Path, Json(data): Json, ) -> ApiResult { @@ -53,7 +56,7 @@ pub async fn add_api_token( // all API tokens start with a `dg-` prefix let token_string = format!("dg-{}", gen_alphanumeric(API_TOKEN_LENGTH)); - ApiToken::new( + let token = ApiToken::new( user.id, Utc::now().naive_utc(), data.name.clone(), @@ -63,7 +66,15 @@ pub async fn add_api_token( .await?; info!("Added new API token {} for user {username}", data.name); - + if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::ApiTokenAdded { + owner, + token_name: token.name.clone(), + }, + })?; + } Ok(ApiResponse { json: json!({"token": token_string}), status: StatusCode::CREATED, @@ -96,6 +107,7 @@ pub async fn delete_api_token( _admin: AdminRole, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Path((username, token_id)): Path<(String, i64)>, ) -> ApiResult { debug!("Removing API token {token_id} for user {username}"); @@ -104,7 +116,20 @@ pub async fn delete_api_token( if !session.is_admin && user.id != token.user_id { return Err(WebError::Forbidden(String::new())); } - token.delete(&appstate.pool).await?; + token.clone().delete(&appstate.pool).await?; + if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::ApiTokenRemoved { + owner, + token_name: token.name.clone(), + }, + })?; + } + info!( + "User {} removed API token {}({token_id}) for user {username}", + user.username, token.name + ); } else { error!("API token with id {token_id} not found"); return Err(WebError::BadRequest("Key not found".into())); @@ -126,16 +151,34 @@ pub async fn rename_api_token( _admin: AdminRole, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Path((username, token_id)): Path<(String, i64)>, Json(data): Json, ) -> ApiResult { + debug!("Renaming API token {token_id} for user {username}"); let user = user_for_admin_or_self(&appstate.pool, &session, &username).await?; if let Some(mut token) = ApiToken::find_by_id(&appstate.pool, token_id).await? { if !session.is_admin && user.id != token.user_id { return Err(WebError::Forbidden(String::new())); } + let old_name = token.name.clone(); token.name = data.name; + let new_name = token.name.clone(); token.save(&appstate.pool).await?; + if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::ApiTokenRenamed { + owner, + old_name, + new_name, + }, + })?; + } + info!( + "User {} renamed API token {}({token_id}) for user {username}", + user.username, token.name + ); } else { error!("User {username} tried to rename non-existing API token with id {token_id}",); return Err(WebError::ObjectNotFound(String::new())); diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 01d1a2ac7..9d3879040 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -1,6 +1,6 @@ use std::net::IpAddr; -use crate::db::{Device, Id, MFAMethod, WireguardNetwork}; +use crate::db::{Device, Id, MFAMethod, User, WireguardNetwork}; use chrono::{NaiveDateTime, Utc}; /// Shared context that needs to be added to every API event @@ -151,6 +151,19 @@ pub enum ApiEventType { VpnLocationModified { location: WireguardNetwork, }, + ApiTokenAdded { + owner: User, + token_name: String, + }, + ApiTokenRemoved { + owner: User, + token_name: String, + }, + ApiTokenRenamed { + owner: User, + old_name: String, + new_name: String, + }, } /// Events from Web API diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 0477e0995..7846bfac7 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -10,12 +10,12 @@ use tracing::{debug, error, info, trace}; use defguard_core::db::{ models::audit_log::{ metadata::{ - AuditStreamMetadata, DeviceAddedMetadata, DeviceModifiedMetadata, - DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, MfaLoginMetadata, - MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, - NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, UserAddedMetadata, - UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, - VpnLocationMetadata, + ApiTokenMetadata, ApiTokenRenamedMetadata, AuditStreamMetadata, DeviceAddedMetadata, + DeviceModifiedMetadata, DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, + MfaLoginMetadata, MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, + NetworkDeviceAddedMetadata, NetworkDeviceModifiedMetadata, + NetworkDeviceRemovedMetadata, UserAddedMetadata, UserModifiedMetadata, + UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -151,18 +151,27 @@ pub async fn run_event_logger( key_name: _, key_type: _, } => todo!(), - DefguardEvent::ApiTokenAdded { - token_id: _, - token_name: _, - } => todo!(), - DefguardEvent::ApiTokenRemoved { - token_id: _, - token_name: _, - } => todo!(), + DefguardEvent::ApiTokenAdded { owner, token_name } => ( + EventType::ApiTokenAdded, + serde_json::to_value(ApiTokenMetadata { owner, token_name }).ok(), + ), + DefguardEvent::ApiTokenRemoved { owner, token_name } => ( + EventType::ApiTokenRemoved, + serde_json::to_value(ApiTokenMetadata { owner, token_name }).ok(), + ), DefguardEvent::ApiTokenRenamed { - token_id: _, - token_name: _, - } => todo!(), + owner, + old_name, + new_name, + } => ( + EventType::ApiTokenRenamed, + serde_json::to_value(ApiTokenRenamedMetadata { + owner, + old_name, + new_name, + }) + .ok(), + ), DefguardEvent::UserAdded { username } => ( EventType::UserAdded, serde_json::to_value(UserAddedMetadata { username }).ok(), diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 845537e08..56694d7ee 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -3,7 +3,8 @@ use std::net::IpAddr; use defguard_core::{ db::{ - models::authentication_key::AuthenticationKeyType, Device, Id, MFAMethod, WireguardNetwork, + models::authentication_key::AuthenticationKeyType, Device, Id, MFAMethod, User, + WireguardNetwork, }, events::{ApiRequestContext, BidiRequestContext, GrpcRequestContext, InternalEventContext}, }; @@ -133,16 +134,17 @@ pub enum DefguardEvent { }, // API token management ApiTokenAdded { - token_id: Id, + owner: User, token_name: String, }, ApiTokenRemoved { - token_id: Id, + owner: User, token_name: String, }, ApiTokenRenamed { - token_id: Id, - token_name: String, + owner: User, + old_name: String, + new_name: String, }, // user management UserAdded { diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 9040c6ac4..e3909fd6a 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -144,6 +144,21 @@ impl EventRouter { ApiEventType::VpnLocationModified { location } => { LoggerEvent::Defguard(DefguardEvent::VpnLocationModified { location }) } + ApiEventType::ApiTokenAdded { owner, token_name } => { + LoggerEvent::Defguard(DefguardEvent::ApiTokenAdded { owner, token_name }) + } + ApiEventType::ApiTokenRemoved { owner, token_name } => { + LoggerEvent::Defguard(DefguardEvent::ApiTokenRemoved { owner, token_name }) + } + ApiEventType::ApiTokenRenamed { + owner, + old_name, + new_name, + } => LoggerEvent::Defguard(DefguardEvent::ApiTokenRenamed { + owner, + old_name, + new_name, + }), }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 4d7bb0e0f..43d59a8e5 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2584,6 +2584,9 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T vpn_location_added: 'VPN location added', vpn_location_removed: 'VPN location removed', vpn_location_modified: 'VPN location modified', + api_token_added: 'API token added', + api_token_removed: 'API token removed', + api_token_renamed: 'API token renamed', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 9582ad9d6..56a4518ed 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6250,6 +6250,18 @@ type RootTranslation = { * V​P​N​ ​l​o​c​a​t​i​o​n​ ​m​o​d​i​f​i​e​d */ vpn_location_modified: string + /** + * A​P​I​ ​t​o​k​e​n​ ​a​d​d​e​d + */ + api_token_added: string + /** + * A​P​I​ ​t​o​k​e​n​ ​r​e​m​o​v​e​d + */ + api_token_removed: string + /** + * A​P​I​ ​t​o​k​e​n​ ​r​e​n​a​m​e​d + */ + api_token_renamed: string } auditModule: { /** @@ -12447,6 +12459,18 @@ export type TranslationFunctions = { * VPN location modified */ vpn_location_modified: () => LocalizedString + /** + * API token added + */ + api_token_added: () => LocalizedString + /** + * API token removed + */ + api_token_removed: () => LocalizedString + /** + * API token renamed + */ + api_token_renamed: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 9b29c112a..38e1f2bf6 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -46,7 +46,10 @@ export type AuditEventType = | 'password_reset_completed' | 'vpn_location_added' | 'vpn_location_removed' - | 'vpn_location_modified'; + | 'vpn_location_modified' + | 'api_token_added' + | 'api_token_removed' + | 'api_token_renamed'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -88,4 +91,7 @@ export const auditEventTypeValues: AuditEventType[] = [ 'vpn_location_added', 'vpn_location_removed', 'vpn_location_modified', + 'api_token_added', + 'api_token_removed', + 'api_token_renamed', ]; From e9d6db1d0b9bf9dfb9666e122f70949733e4d068 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 13 Jun 2025 13:31:29 +0200 Subject: [PATCH 04/23] OpenId client app events --- .../src/db/models/audit_log/metadata.rs | 13 ++++ .../src/db/models/audit_log/mod.rs | 1 + .../src/db/models/oauth2client.rs | 2 +- crates/defguard_core/src/events.rs | 17 +++++ .../src/handlers/openid_clients.rs | 74 ++++++++++++++----- crates/defguard_event_logger/src/lib.rs | 47 +++++++----- crates/defguard_event_logger/src/message.rs | 3 +- .../defguard_event_router/src/handlers/api.rs | 18 +++++ web/src/i18n/en/index.ts | 4 + web/src/i18n/i18n-types.ts | 32 ++++++++ web/src/pages/activity/types.ts | 10 ++- 11 files changed, 181 insertions(+), 40 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 9a21ae880..563ad1002 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -112,3 +112,16 @@ pub struct ApiTokenRenamedMetadata { pub old_name: String, pub new_name: String, } + +#[derive(Serialize)] +pub struct OpenIdAppMetadata { + pub app_id: Id, + pub app_name: String, +} + +#[derive(Serialize)] +pub struct OpenIdAppStateChangedMetadata { + pub app_id: Id, + pub app_name: String, + pub enabled: bool, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index 71f3b9670..c623f2aae 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -58,6 +58,7 @@ pub enum EventType { OpenIdAppAdded, OpenIdAppRemoved, OpenIdAppModified, + OpenIdAppStateChanged, // VPN location management VpnLocationAdded, VpnLocationRemoved, diff --git a/crates/defguard_core/src/db/models/oauth2client.rs b/crates/defguard_core/src/db/models/oauth2client.rs index 91d40f9a2..41c960178 100644 --- a/crates/defguard_core/src/db/models/oauth2client.rs +++ b/crates/defguard_core/src/db/models/oauth2client.rs @@ -7,7 +7,7 @@ use crate::{ random::gen_alphanumeric, }; -#[derive(Deserialize, Model, Serialize)] +#[derive(Clone, Deserialize, Model, Serialize)] pub struct OAuth2Client { pub id: I, pub client_id: String, // unique diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 9d3879040..5563102d3 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -164,6 +164,23 @@ pub enum ApiEventType { old_name: String, new_name: String, }, + OpenIdAppAdded { + app_id: Id, + app_name: String, + }, + OpenIdAppRemoved { + app_id: Id, + app_name: String, + }, + OpenIdAppModified { + app_id: Id, + app_name: String, + }, + OpenIdAppStateChanged { + app_id: Id, + app_name: String, + enabled: bool, + }, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/openid_clients.rs b/crates/defguard_core/src/handlers/openid_clients.rs index b9d2132ea..41f6e2e59 100644 --- a/crates/defguard_core/src/handlers/openid_clients.rs +++ b/crates/defguard_core/src/handlers/openid_clients.rs @@ -12,23 +12,32 @@ use crate::{ oauth2client::{OAuth2Client, OAuth2ClientSafe}, NewOpenIDClient, }, + events::{ApiEvent, ApiEventType, ApiRequestContext}, }; pub async fn add_openid_client( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Json(data): Json, ) -> ApiResult { - let client = OAuth2Client::from_new(data).save(&appstate.pool).await?; debug!( "User {} adding OpenID client {}", - session.user.username, client.name + session.user.username, data.name ); + let client = OAuth2Client::from_new(data).save(&appstate.pool).await?; info!( "User {} added OpenID client {}", session.user.username, client.name ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::OpenIdAppAdded { + app_id: client.id, + app_name: client.name.clone(), + }, + })?; Ok(ApiResponse { json: json!(client), status: StatusCode::CREATED, @@ -36,9 +45,9 @@ pub async fn add_openid_client( } pub async fn list_openid_clients(_admin: AdminRole, State(appstate): State) -> ApiResult { - let openid_clients = OAuth2Client::all(&appstate.pool).await?; + let clients = OAuth2Client::all(&appstate.pool).await?; Ok(ApiResponse { - json: json!(openid_clients), + json: json!(clients), status: StatusCode::OK, }) } @@ -49,15 +58,15 @@ pub async fn get_openid_client( session: SessionInfo, ) -> ApiResult { match OAuth2Client::find_by_client_id(&appstate.pool, &client_id).await? { - Some(openid_client) => { + Some(client) => { if session.is_admin { Ok(ApiResponse { - json: json!(openid_client), + json: json!(client), status: StatusCode::OK, }) } else { Ok(ApiResponse { - json: json!(OAuth2ClientSafe::from(openid_client)), + json: json!(OAuth2ClientSafe::from(client)), status: StatusCode::OK, }) } @@ -72,6 +81,7 @@ pub async fn get_openid_client( pub async fn change_openid_client( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(client_id): Path, Json(data): Json, @@ -81,16 +91,23 @@ pub async fn change_openid_client( session.user.username ); let status = match OAuth2Client::find_by_client_id(&appstate.pool, &client_id).await? { - Some(mut openid_client) => { - openid_client.name = data.name; - openid_client.redirect_uri = data.redirect_uri; - openid_client.enabled = data.enabled; - openid_client.scope = data.scope; - openid_client.save(&appstate.pool).await?; + Some(mut client) => { + client.name = data.name; + client.redirect_uri = data.redirect_uri; + client.enabled = data.enabled; + client.scope = data.scope; + client.save(&appstate.pool).await?; info!( "User {} updated OpenID client {client_id} ({})", - session.user.username, openid_client.name + session.user.username, client.name ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::OpenIdAppModified { + app_id: client.id, + app_name: client.name, + }, + })?; StatusCode::OK } None => StatusCode::NOT_FOUND, @@ -104,6 +121,7 @@ pub async fn change_openid_client( pub async fn change_openid_client_state( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(client_id): Path, Json(data): Json, @@ -113,13 +131,21 @@ pub async fn change_openid_client_state( session.user.username ); let status = match OAuth2Client::find_by_client_id(&appstate.pool, &client_id).await? { - Some(mut openid_client) => { - openid_client.enabled = data.enabled; - openid_client.save(&appstate.pool).await?; + Some(mut client) => { + client.enabled = data.enabled; + client.save(&appstate.pool).await?; info!( "User {} updated OpenID client {client_id} ({}) enabled state to {}", - session.user.username, openid_client.name, openid_client.enabled, + session.user.username, client.name, client.enabled, ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::OpenIdAppStateChanged { + app_id: client.id, + app_name: client.name, + enabled: client.enabled, + }, + })?; StatusCode::OK } None => StatusCode::NOT_FOUND, @@ -133,6 +159,7 @@ pub async fn change_openid_client_state( pub async fn delete_openid_client( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(client_id): Path, ) -> ApiResult { @@ -141,12 +168,19 @@ pub async fn delete_openid_client( session.user.username ); let status = match OAuth2Client::find_by_client_id(&appstate.pool, &client_id).await? { - Some(openid_client) => { - openid_client.delete(&appstate.pool).await?; + Some(client) => { + client.clone().delete(&appstate.pool).await?; info!( "User {} deleted OpenID client {client_id}", session.user.username ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::OpenIdAppRemoved { + app_id: client.id, + app_name: client.name, + }, + })?; StatusCode::OK } None => StatusCode::NOT_FOUND, diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 7846bfac7..41a9e4d9a 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -14,8 +14,9 @@ use defguard_core::db::{ DeviceModifiedMetadata, DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, MfaLoginMetadata, MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, NetworkDeviceModifiedMetadata, - NetworkDeviceRemovedMetadata, UserAddedMetadata, UserModifiedMetadata, - UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, + NetworkDeviceRemovedMetadata, OpenIdAppMetadata, OpenIdAppStateChangedMetadata, + UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, + VpnClientMfaMetadata, VpnLocationMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -242,22 +243,34 @@ pub async fn run_event_logger( EventType::VpnLocationModified, serde_json::to_value(VpnLocationMetadata { location }).ok(), ), - DefguardEvent::OpenIdAppAdded { - app_id: _, - app_name: _, - } => todo!(), - DefguardEvent::OpenIdAppRemoved { - app_id: _, - app_name: _, - } => todo!(), + DefguardEvent::OpenIdAppAdded { app_id, app_name } => ( + EventType::OpenIdAppAdded, + serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), + ), + DefguardEvent::OpenIdAppRemoved { app_id, app_name } => ( + EventType::OpenIdAppRemoved, + serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), + ), DefguardEvent::OpenIdAppModified { - app_id: _, - app_name: _, - } => todo!(), - DefguardEvent::OpenIdAppDisabled { - app_id: _, - app_name: _, - } => todo!(), + app_id, + app_name, + } => ( + EventType::OpenIdAppModified, + serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), + ), + DefguardEvent::OpenIdAppStateChanged { + app_id, + app_name, + enabled, + } => ( + EventType::OpenIdAppStateChanged, + serde_json::to_value(OpenIdAppStateChangedMetadata { + app_id, + app_name, + enabled, + }) + .ok(), + ), DefguardEvent::OpenIdProviderAdded { provider_id: _, provider_name: _, diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 56694d7ee..13a02e093 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -216,9 +216,10 @@ pub enum DefguardEvent { app_id: Id, app_name: String, }, - OpenIdAppDisabled { + OpenIdAppStateChanged { app_id: Id, app_name: String, + enabled: bool, }, // OpenID provider management OpenIdProviderAdded { diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index e3909fd6a..bcddaf703 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -159,6 +159,24 @@ impl EventRouter { old_name, new_name, }), + ApiEventType::OpenIdAppAdded { app_id, app_name } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppAdded { app_id, app_name }) + } + ApiEventType::OpenIdAppRemoved { app_id, app_name } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppRemoved { app_id, app_name }) + } + ApiEventType::OpenIdAppModified { app_id, app_name } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppModified { app_id, app_name }) + } + ApiEventType::OpenIdAppStateChanged { + app_id, + app_name, + enabled, + } => LoggerEvent::Defguard(DefguardEvent::OpenIdAppStateChanged { + app_id, + app_name, + enabled, + }), }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 43d59a8e5..6d28d32f5 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2587,6 +2587,10 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T api_token_added: 'API token added', api_token_removed: 'API token removed', api_token_renamed: 'API token renamed', + open_id_app_added: 'OpenID app added', + open_id_app_removed: 'OpenID app removed', + open_id_app_modified: 'OpenID app modified', + open_id_app_state_changed: 'OpenID app state changed', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 56a4518ed..00eebf461 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6262,6 +6262,22 @@ type RootTranslation = { * A​P​I​ ​t​o​k​e​n​ ​r​e​n​a​m​e​d */ api_token_renamed: string + /** + * O​p​e​n​I​D​ ​a​p​p​ ​a​d​d​e​d + */ + open_id_app_added: string + /** + * O​p​e​n​I​D​ ​a​p​p​ ​r​e​m​o​v​e​d + */ + open_id_app_removed: string + /** + * O​p​e​n​I​D​ ​a​p​p​ ​m​o​d​i​f​i​e​d + */ + open_id_app_modified: string + /** + * O​p​e​n​I​D​ ​a​p​p​ ​s​t​a​t​e​ ​c​h​a​n​g​e​d + */ + open_id_app_state_changed: string } auditModule: { /** @@ -12471,6 +12487,22 @@ export type TranslationFunctions = { * API token renamed */ api_token_renamed: () => LocalizedString + /** + * OpenID app added + */ + open_id_app_added: () => LocalizedString + /** + * OpenID app removed + */ + open_id_app_removed: () => LocalizedString + /** + * OpenID app modified + */ + open_id_app_modified: () => LocalizedString + /** + * OpenID app state changed + */ + open_id_app_state_changed: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 38e1f2bf6..ee63cb144 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -49,7 +49,11 @@ export type AuditEventType = | 'vpn_location_modified' | 'api_token_added' | 'api_token_removed' - | 'api_token_renamed'; + | 'api_token_renamed' + | 'open_id_app_added' + | 'open_id_app_removed' + | 'open_id_app_modified' + | 'open_id_app_state_changed'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -94,4 +98,8 @@ export const auditEventTypeValues: AuditEventType[] = [ 'api_token_added', 'api_token_removed', 'api_token_renamed', + 'open_id_app_added', + 'open_id_app_removed', + 'open_id_app_modified', + 'open_id_app_state_changed', ]; From 817e9c7873e504ee2eb589fb818da91483021692 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Jun 2025 07:48:59 +0200 Subject: [PATCH 05/23] OpenIdProvider events --- .../src/db/models/audit_log/metadata.rs | 6 ++ .../src/db/models/audit_log/mod.rs | 3 + .../enterprise/handlers/openid_providers.rs | 68 +++++++------------ crates/defguard_core/src/events.rs | 8 +++ crates/defguard_event_logger/src/lib.rs | 37 ++++++---- crates/defguard_event_logger/src/message.rs | 2 +- .../defguard_event_router/src/handlers/api.rs | 14 ++++ web/src/i18n/en/index.ts | 2 + web/src/i18n/i18n-types.ts | 16 +++++ web/src/pages/activity/types.ts | 6 +- 10 files changed, 104 insertions(+), 58 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 563ad1002..95d62f84f 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -125,3 +125,9 @@ pub struct OpenIdAppStateChangedMetadata { pub app_name: String, pub enabled: bool, } + +#[derive(Serialize)] +pub struct OpenIdProviderMetadata { + pub provider_id: Id, + pub provider_name: String, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index c623f2aae..de76b0652 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -59,6 +59,9 @@ pub enum EventType { OpenIdAppRemoved, OpenIdAppModified, OpenIdAppStateChanged, + // OpenID provider management + OpenIdProviderRemoved, + OpenIdProviderModified, // VPN location management VpnLocationAdded, VpnLocationRemoved, diff --git a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs index 143a210e8..256fba10e 100644 --- a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs +++ b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs @@ -17,6 +17,7 @@ use crate::{ enterprise::{ db::models::openid_provider::OpenIdProvider, directory_sync::test_directory_sync_connection, }, + events::{ApiEvent, ApiEventType, ApiRequestContext}, handlers::{ApiResponse, ApiResult}, }; @@ -51,9 +52,14 @@ pub async fn add_openid_provider( _license: LicenseInfo, _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Json(provider_data): Json, ) -> ApiResult { + debug!( + "User {} adding OpenID provider {}", + session.user.username, provider_data.name + ); let current_provider = OpenIdProvider::get_current(&appstate.pool).await?; // The key is sent from the frontend only when user explicitly changes it, as we never send it back. @@ -148,14 +154,17 @@ pub async fn add_openid_provider( ) .upsert(&appstate.pool) .await?; - debug!( - "User {} adding OpenID provider {}", - session.user.username, new_provider.name - ); info!( "User {} added OpenID client {}", session.user.username, new_provider.name ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::OpenIdProviderModified { + provider_id: new_provider.id, + provider_name: new_provider.name, + }, + })?; Ok(ApiResponse { json: json!({}), @@ -197,6 +206,7 @@ pub async fn delete_openid_provider( _license: LicenseInfo, _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(provider_data): Path, ) -> ApiResult { @@ -206,11 +216,20 @@ pub async fn delete_openid_provider( ); let provider = OpenIdProvider::find_by_name(&appstate.pool, &provider_data.name).await?; if let Some(provider) = provider { + let provider_id = provider.id; + let provider_name = provider.name.clone(); provider.delete(&appstate.pool).await?; info!( - "User {} deleted OpenID provider {}", - session.user.username, provider_data.name + "User {} deleted OpenID provider {provider_name}", + session.user.username ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::OpenIdProviderRemoved { + provider_id, + provider_name, + }, + })?; Ok(ApiResponse { json: json!({}), status: StatusCode::OK, @@ -227,43 +246,6 @@ pub async fn delete_openid_provider( } } -pub async fn modify_openid_provider( - _license: LicenseInfo, - _admin: AdminRole, - session: SessionInfo, - State(appstate): State, - Json(provider_data): Json, -) -> ApiResult { - debug!( - "User {} modifying OpenID provider {}", - session.user.username, provider_data.name - ); - let provider = OpenIdProvider::find_by_name(&appstate.pool, &provider_data.name).await?; - if let Some(mut provider) = provider { - provider.base_url = provider_data.base_url; - provider.client_id = provider_data.client_id; - provider.client_secret = provider_data.client_secret; - provider.save(&appstate.pool).await?; - info!( - "User {} modified OpenID client {}", - session.user.username, provider.name - ); - Ok(ApiResponse { - json: json!({}), - status: StatusCode::OK, - }) - } else { - warn!( - "User {} failed to modify OpenID client {}. Such client does not exist.", - session.user.username, provider_data.name - ); - Ok(ApiResponse { - json: json!({}), - status: StatusCode::NOT_FOUND, - }) - } -} - pub async fn list_openid_providers( _license: LicenseInfo, _admin: AdminRole, diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 5563102d3..70cd93c09 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -181,6 +181,14 @@ pub enum ApiEventType { app_name: String, enabled: bool, }, + OpenIdProviderModified { + provider_id: Id, + provider_name: String, + }, + OpenIdProviderRemoved { + provider_id: Id, + provider_name: String, + }, } /// Events from Web API diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 41a9e4d9a..e8e87a747 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -15,8 +15,8 @@ use defguard_core::db::{ MfaLoginMetadata, MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, OpenIdAppMetadata, OpenIdAppStateChangedMetadata, - UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, - VpnClientMfaMetadata, VpnLocationMetadata, + OpenIdProviderMetadata, UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, + VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -251,10 +251,7 @@ pub async fn run_event_logger( EventType::OpenIdAppRemoved, serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), ), - DefguardEvent::OpenIdAppModified { - app_id, - app_name, - } => ( + DefguardEvent::OpenIdAppModified { app_id, app_name } => ( EventType::OpenIdAppModified, serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), ), @@ -271,14 +268,28 @@ pub async fn run_event_logger( }) .ok(), ), - DefguardEvent::OpenIdProviderAdded { - provider_id: _, - provider_name: _, - } => todo!(), + DefguardEvent::OpenIdProviderModified { + provider_id, + provider_name, + } => ( + EventType::OpenIdProviderModified, + serde_json::to_value(OpenIdProviderMetadata { + provider_id, + provider_name, + }) + .ok(), + ), DefguardEvent::OpenIdProviderRemoved { - provider_id: _, - provider_name: _, - } => todo!(), + provider_id, + provider_name, + } => ( + EventType::OpenIdProviderRemoved, + serde_json::to_value(OpenIdProviderMetadata { + provider_id, + provider_name, + }) + .ok(), + ), DefguardEvent::SettingsUpdated => todo!(), DefguardEvent::SettingsUpdatedPartial => todo!(), DefguardEvent::SettingsDefaultBrandingRestored => todo!(), diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 13a02e093..296a10d1c 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -222,7 +222,7 @@ pub enum DefguardEvent { enabled: bool, }, // OpenID provider management - OpenIdProviderAdded { + OpenIdProviderModified { provider_id: Id, provider_name: String, }, diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index bcddaf703..3c51fa0f6 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -177,6 +177,20 @@ impl EventRouter { app_name, enabled, }), + ApiEventType::OpenIdProviderRemoved { + provider_id, + provider_name, + } => LoggerEvent::Defguard(DefguardEvent::OpenIdProviderRemoved { + provider_id, + provider_name, + }), + ApiEventType::OpenIdProviderModified { + provider_id, + provider_name, + } => LoggerEvent::Defguard(DefguardEvent::OpenIdProviderModified { + provider_id, + provider_name, + }), }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 6d28d32f5..52756707d 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2591,6 +2591,8 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T open_id_app_removed: 'OpenID app removed', open_id_app_modified: 'OpenID app modified', open_id_app_state_changed: 'OpenID app state changed', + open_id_provider_removed: 'OpenID provider removed', + open_id_provider_modified: 'OpenID provider modified', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 00eebf461..cf7ae0b33 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6278,6 +6278,14 @@ type RootTranslation = { * O​p​e​n​I​D​ ​a​p​p​ ​s​t​a​t​e​ ​c​h​a​n​g​e​d */ open_id_app_state_changed: string + /** + * O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​ ​r​e​m​o​v​e​d + */ + open_id_provider_removed: string + /** + * O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​ ​m​o​d​i​f​i​e​d + */ + open_id_provider_modified: string } auditModule: { /** @@ -12503,6 +12511,14 @@ export type TranslationFunctions = { * OpenID app state changed */ open_id_app_state_changed: () => LocalizedString + /** + * OpenID provider removed + */ + open_id_provider_removed: () => LocalizedString + /** + * OpenID provider modified + */ + open_id_provider_modified: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index ee63cb144..1e53d781c 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -53,7 +53,9 @@ export type AuditEventType = | 'open_id_app_added' | 'open_id_app_removed' | 'open_id_app_modified' - | 'open_id_app_state_changed'; + | 'open_id_app_state_changed' + | 'open_id_provider_removed' + | 'open_id_provider_modified'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -102,4 +104,6 @@ export const auditEventTypeValues: AuditEventType[] = [ 'open_id_app_removed', 'open_id_app_modified', 'open_id_app_state_changed', + 'open_id_provider_removed', + 'open_id_provider_modified', ]; From 5845c0d84c410b367c495a027fcad44c511b38bc Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Jun 2025 08:37:44 +0200 Subject: [PATCH 06/23] Settings events --- .../defguard_core/src/db/models/audit_log/mod.rs | 3 +++ crates/defguard_core/src/events.rs | 2 ++ crates/defguard_core/src/handlers/settings.rs | 11 +++++++++++ crates/defguard_event_logger/src/lib.rs | 6 ++++-- crates/defguard_event_router/src/handlers/api.rs | 4 ++++ web/src/i18n/en/index.ts | 2 ++ web/src/i18n/i18n-types.ts | 16 ++++++++++++++++ web/src/pages/activity/types.ts | 6 +++++- 8 files changed, 47 insertions(+), 3 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index de76b0652..a089299b4 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -83,6 +83,9 @@ pub enum EventType { ApiTokenAdded, ApiTokenRemoved, ApiTokenRenamed, + // Settings management + SettingsUpdated, + SettingsUpdatedPartial, } #[derive(Model, FromRow, Serialize)] diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 70cd93c09..adb610e5f 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -189,6 +189,8 @@ pub enum ApiEventType { provider_id: Id, provider_name: String, }, + SettingsUpdated, + SettingsUpdatedPartial, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/settings.rs b/crates/defguard_core/src/handlers/settings.rs index fad4ca348..e2000951a 100644 --- a/crates/defguard_core/src/handlers/settings.rs +++ b/crates/defguard_core/src/handlers/settings.rs @@ -17,6 +17,7 @@ use crate::{ license::update_cached_license, }, error::WebError, + events::{ApiEvent, ApiEventType, ApiRequestContext}, AppState, }; @@ -47,6 +48,7 @@ pub async fn get_settings(_admin: AdminRole, State(appstate): State) - pub async fn update_settings( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Json(data): Json, ) -> ApiResult { @@ -57,6 +59,10 @@ pub async fn update_settings( update_current_settings(&appstate.pool, data).await?; info!("User {} updated settings", session.user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::SettingsUpdated, + })?; Ok(ApiResponse::default()) } @@ -113,6 +119,7 @@ pub async fn patch_settings( _admin: AdminRole, State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Json(data): Json, ) -> ApiResult { debug!( @@ -150,6 +157,10 @@ pub async fn patch_settings( update_current_settings(&appstate.pool, settings).await?; info!("Admin {} patched settings.", session.user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::SettingsUpdatedPartial, + })?; Ok(ApiResponse::default()) } diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index e8e87a747..8e074c3a6 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -290,8 +290,10 @@ pub async fn run_event_logger( }) .ok(), ), - DefguardEvent::SettingsUpdated => todo!(), - DefguardEvent::SettingsUpdatedPartial => todo!(), + DefguardEvent::SettingsUpdated => (EventType::SettingsUpdated, None), + DefguardEvent::SettingsUpdatedPartial => { + (EventType::SettingsUpdatedPartial, None) + } DefguardEvent::SettingsDefaultBrandingRestored => todo!(), DefguardEvent::AuditStreamCreated { stream_id, diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 3c51fa0f6..4cc37980f 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -191,6 +191,10 @@ impl EventRouter { provider_id, provider_name, }), + ApiEventType::SettingsUpdated => LoggerEvent::Defguard(DefguardEvent::SettingsUpdated), + ApiEventType::SettingsUpdatedPartial => { + LoggerEvent::Defguard(DefguardEvent::SettingsUpdatedPartial) + } }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 52756707d..475d2c22d 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2593,6 +2593,8 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T open_id_app_state_changed: 'OpenID app state changed', open_id_provider_removed: 'OpenID provider removed', open_id_provider_modified: 'OpenID provider modified', + settings_updated: 'Settings updated', + settings_updated_partial: 'Settings partially updated', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index cf7ae0b33..bc6884806 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6286,6 +6286,14 @@ type RootTranslation = { * O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​ ​m​o​d​i​f​i​e​d */ open_id_provider_modified: string + /** + * S​e​t​t​i​n​g​s​ ​u​p​d​a​t​e​d + */ + settings_updated: string + /** + * S​e​t​t​i​n​g​s​ ​p​a​r​t​i​a​l​l​y​ ​u​p​d​a​t​e​d + */ + settings_updated_partial: string } auditModule: { /** @@ -12519,6 +12527,14 @@ export type TranslationFunctions = { * OpenID provider modified */ open_id_provider_modified: () => LocalizedString + /** + * Settings updated + */ + settings_updated: () => LocalizedString + /** + * Settings partially updated + */ + settings_updated_partial: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 1e53d781c..9f17ac4cf 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -55,7 +55,9 @@ export type AuditEventType = | 'open_id_app_modified' | 'open_id_app_state_changed' | 'open_id_provider_removed' - | 'open_id_provider_modified'; + | 'open_id_provider_modified' + | 'settings_updated' + | 'settings_updated_partial'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -106,4 +108,6 @@ export const auditEventTypeValues: AuditEventType[] = [ 'open_id_app_state_changed', 'open_id_provider_removed', 'open_id_provider_modified', + 'settings_updated', + 'settings_updated_partial', ]; From 33ccc82773b6e6981351a1705443684316074089 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Jun 2025 08:48:09 +0200 Subject: [PATCH 07/23] Restore default settings event --- crates/defguard_core/src/db/models/audit_log/mod.rs | 1 + crates/defguard_core/src/events.rs | 1 + crates/defguard_core/src/handlers/settings.rs | 5 +++++ crates/defguard_event_logger/src/lib.rs | 4 +++- crates/defguard_event_router/src/handlers/api.rs | 3 +++ web/src/i18n/en/index.ts | 1 + web/src/i18n/i18n-types.ts | 8 ++++++++ web/src/pages/activity/types.ts | 4 +++- 8 files changed, 25 insertions(+), 2 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index a089299b4..dcd05f19a 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -86,6 +86,7 @@ pub enum EventType { // Settings management SettingsUpdated, SettingsUpdatedPartial, + SettingsDefaultBrandingRestored, } #[derive(Model, FromRow, Serialize)] diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index adb610e5f..ae4775fe5 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -191,6 +191,7 @@ pub enum ApiEventType { }, SettingsUpdated, SettingsUpdatedPartial, + SettingsDefaultBrandingRestored, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/settings.rs b/crates/defguard_core/src/handlers/settings.rs index e2000951a..5ae0ccc8a 100644 --- a/crates/defguard_core/src/handlers/settings.rs +++ b/crates/defguard_core/src/handlers/settings.rs @@ -90,6 +90,7 @@ pub async fn set_default_branding( State(appstate): State, Path(_id): Path, // TODO: check with front-end and remove. session: SessionInfo, + context: ApiRequestContext, ) -> ApiResult { debug!( "User {} restoring default branding settings", @@ -106,6 +107,10 @@ pub async fn set_default_branding( "User {} restored default branding settings", session.user.username ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::SettingsDefaultBrandingRestored, + })?; Ok(ApiResponse { json: json!(settings), status: StatusCode::OK, diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 8e074c3a6..bb766f1b0 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -294,7 +294,9 @@ pub async fn run_event_logger( DefguardEvent::SettingsUpdatedPartial => { (EventType::SettingsUpdatedPartial, None) } - DefguardEvent::SettingsDefaultBrandingRestored => todo!(), + DefguardEvent::SettingsDefaultBrandingRestored => { + (EventType::SettingsDefaultBrandingRestored, None) + } DefguardEvent::AuditStreamCreated { stream_id, stream_name, diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 4cc37980f..047564e01 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -195,6 +195,9 @@ impl EventRouter { ApiEventType::SettingsUpdatedPartial => { LoggerEvent::Defguard(DefguardEvent::SettingsUpdatedPartial) } + ApiEventType::SettingsDefaultBrandingRestored => { + LoggerEvent::Defguard(DefguardEvent::SettingsDefaultBrandingRestored) + } }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 475d2c22d..dc0e80959 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2595,6 +2595,7 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T open_id_provider_modified: 'OpenID provider modified', settings_updated: 'Settings updated', settings_updated_partial: 'Settings partially updated', + settings_default_branding_restored: 'Default branding restored', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index bc6884806..7a71f9573 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6294,6 +6294,10 @@ type RootTranslation = { * S​e​t​t​i​n​g​s​ ​p​a​r​t​i​a​l​l​y​ ​u​p​d​a​t​e​d */ settings_updated_partial: string + /** + * D​e​f​a​u​l​t​ ​b​r​a​n​d​i​n​g​ ​r​e​s​t​o​r​e​d + */ + settings_default_branding_restored: string } auditModule: { /** @@ -12535,6 +12539,10 @@ export type TranslationFunctions = { * Settings partially updated */ settings_updated_partial: () => LocalizedString + /** + * Default branding restored + */ + settings_default_branding_restored: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 9f17ac4cf..04534ed2a 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -57,7 +57,8 @@ export type AuditEventType = | 'open_id_provider_removed' | 'open_id_provider_modified' | 'settings_updated' - | 'settings_updated_partial'; + | 'settings_updated_partial' + | 'settings_default_branding_restored'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -110,4 +111,5 @@ export const auditEventTypeValues: AuditEventType[] = [ 'open_id_provider_modified', 'settings_updated', 'settings_updated_partial', + 'settings_default_branding_restored', ]; From 37b60e9a56cf152d9e531b3bf39b7bd71ea82d25 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Jun 2025 10:18:18 +0200 Subject: [PATCH 08/23] Group management events --- .../src/db/models/audit_log/metadata.rs | 19 +++++++- .../src/db/models/audit_log/mod.rs | 7 +++ crates/defguard_core/src/db/models/group.rs | 2 +- crates/defguard_core/src/events.rs | 23 ++++++++- crates/defguard_core/src/handlers/group.rs | 33 ++++++++++++- crates/defguard_event_logger/src/lib.rs | 36 ++++++++++++-- crates/defguard_event_logger/src/message.rs | 24 +++++++++- .../defguard_event_router/src/handlers/api.rs | 18 +++++++ web/src/i18n/en/index.ts | 6 +++ web/src/i18n/i18n-types.ts | 48 +++++++++++++++++++ web/src/pages/activity/types.ts | 14 +++++- 11 files changed, 219 insertions(+), 11 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 95d62f84f..f23c9d293 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -1,4 +1,4 @@ -use crate::db::{Device, Id, MFAMethod, User, WireguardNetwork}; +use crate::db::{Device, Group, Id, MFAMethod, User, WireguardNetwork}; #[derive(Serialize)] pub struct MfaLoginMetadata { @@ -131,3 +131,20 @@ pub struct OpenIdProviderMetadata { pub provider_id: Id, pub provider_name: String, } + +#[derive(Serialize)] +pub struct GroupsBulkAssignedMetadata { + pub users: Vec>, + pub groups: Vec>, +} + +#[derive(Serialize)] +pub struct GroupMetadata { + pub group: Group, +} + +#[derive(Serialize)] +pub struct GroupAssignedMetadata { + pub group: Group, + pub user: User, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index dcd05f19a..1181dd254 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -87,6 +87,13 @@ pub enum EventType { SettingsUpdated, SettingsUpdatedPartial, SettingsDefaultBrandingRestored, + // Groups management + GroupsBulkAssigned, + GroupAdded, + GroupModified, + GroupRemoved, + GroupMemberAdded, + GroupMemberRemoved, } #[derive(Model, FromRow, Serialize)] diff --git a/crates/defguard_core/src/db/models/group.rs b/crates/defguard_core/src/db/models/group.rs index 4e03336c2..634929fcd 100644 --- a/crates/defguard_core/src/db/models/group.rs +++ b/crates/defguard_core/src/db/models/group.rs @@ -19,7 +19,7 @@ impl fmt::Display for Permission { } } -#[derive(Clone, Debug, Model, ToSchema, FromRow, PartialEq)] +#[derive(Clone, Debug, Model, ToSchema, FromRow, PartialEq, Serialize)] pub struct Group { pub(crate) id: I, pub name: String, diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index ae4775fe5..094335741 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -1,6 +1,6 @@ use std::net::IpAddr; -use crate::db::{Device, Id, MFAMethod, User, WireguardNetwork}; +use crate::db::{Device, Group, Id, MFAMethod, User, WireguardNetwork}; use chrono::{NaiveDateTime, Utc}; /// Shared context that needs to be added to every API event @@ -192,6 +192,27 @@ pub enum ApiEventType { SettingsUpdated, SettingsUpdatedPartial, SettingsDefaultBrandingRestored, + GroupsBulkAssigned { + users: Vec>, + groups: Vec>, + }, + GroupAdded { + group: Group, + }, + GroupModified { + group: Group, + }, + GroupRemoved { + group: Group, + }, + GroupMemberAdded { + group: Group, + user: User, + }, + GroupMemberRemoved { + group: Group, + user: User, + }, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/group.rs b/crates/defguard_core/src/handlers/group.rs index d17b528e9..a4ab009f9 100644 --- a/crates/defguard_core/src/handlers/group.rs +++ b/crates/defguard_core/src/handlers/group.rs @@ -19,6 +19,7 @@ use crate::{ ldap_update_users_state, }, error::WebError, + events::{ApiEvent, ApiEventType, ApiRequestContext}, hashset, }; @@ -66,6 +67,7 @@ pub(crate) struct BulkAssignToGroupsRequest { pub(crate) async fn bulk_assign_to_groups( _role: AdminRole, State(appstate): State, + context: ApiRequestContext, Json(data): Json, ) -> Result { debug!("Assigning groups to users."); @@ -123,6 +125,10 @@ pub(crate) async fn bulk_assign_to_groups( ldap_update_users_state(users_to_maybe_update, &appstate.pool).await; info!("Assigned {} groups to {} users.", groups.len(), users.len()); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::GroupsBulkAssigned { users, groups }, + })?; Ok(ApiResponse { json: json!({}), @@ -307,6 +313,7 @@ pub(crate) async fn get_group( pub(crate) async fn create_group( _role: AdminRole, State(appstate): State, + context: ApiRequestContext, Json(group_info): Json, ) -> Result { debug!("Creating group {}", group_info.name); @@ -350,6 +357,10 @@ pub(crate) async fn create_group( } info!("Created group {}", group_info.name); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::GroupAdded { group }, + })?; Ok(ApiResponse { json: json!(group_info), @@ -382,6 +393,7 @@ pub(crate) async fn create_group( pub(crate) async fn modify_group( _role: AdminRole, State(appstate): State, + context: ApiRequestContext, Path(name): Path, Json(group_info): Json, ) -> Result { @@ -477,6 +489,10 @@ pub(crate) async fn modify_group( ldap_update_users_state(affected_users, &appstate.pool).await; info!("Modified group {}", group.name); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::GroupModified { group }, + })?; Ok(ApiResponse::default()) } @@ -507,6 +523,7 @@ pub(crate) async fn modify_group( pub(crate) async fn delete_group( _session: SessionInfo, State(appstate): State, + context: ApiRequestContext, Path(name): Path, ) -> Result { debug!("Deleting group {name}"); @@ -524,7 +541,7 @@ pub(crate) async fn delete_group( }); } } - group.delete(&appstate.pool).await?; + group.clone().delete(&appstate.pool).await?; ldap_delete_group(&name, &appstate.pool).await; // sync allowed devices for all locations @@ -532,6 +549,10 @@ pub(crate) async fn delete_group( WireguardNetwork::sync_all_networks(&mut conn, &appstate.wireguard_tx).await?; info!("Deleted group {name}"); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::GroupRemoved { group }, + })?; Ok(ApiResponse::default()) } else { let msg = format!("Failed to find group {name}"); @@ -568,6 +589,7 @@ pub(crate) async fn delete_group( pub(crate) async fn add_group_member( _role: AdminRole, State(appstate): State, + context: ApiRequestContext, Path(name): Path, Json(data): Json, ) -> Result { @@ -580,6 +602,10 @@ pub(crate) async fn add_group_member( let mut conn = appstate.pool.acquire().await?; WireguardNetwork::sync_all_networks(&mut conn, &appstate.wireguard_tx).await?; info!("Added user: {} to group: {}", user.username, group.name); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::GroupMemberAdded { group, user }, + })?; Ok(ApiResponse::default()) } else { error!("User not found {}", data.username); @@ -623,6 +649,7 @@ pub(crate) async fn add_group_member( pub(crate) async fn remove_group_member( _role: AdminRole, State(appstate): State, + context: ApiRequestContext, Path((name, username)): Path<(String, String)>, ) -> Result { if let Some(group) = Group::find_by_name(&appstate.pool, &name).await? { @@ -638,6 +665,10 @@ pub(crate) async fn remove_group_member( let mut conn = appstate.pool.acquire().await?; WireguardNetwork::sync_all_networks(&mut conn, &appstate.wireguard_tx).await?; info!("Removed user: {} from group: {}", user.username, group.name); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::GroupMemberRemoved { group, user }, + })?; Ok(ApiResponse { json: json!({}), status: StatusCode::OK, diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index bb766f1b0..5366021f7 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -12,11 +12,12 @@ use defguard_core::db::{ metadata::{ ApiTokenMetadata, ApiTokenRenamedMetadata, AuditStreamMetadata, DeviceAddedMetadata, DeviceModifiedMetadata, DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, - MfaLoginMetadata, MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, - NetworkDeviceAddedMetadata, NetworkDeviceModifiedMetadata, - NetworkDeviceRemovedMetadata, OpenIdAppMetadata, OpenIdAppStateChangedMetadata, - OpenIdProviderMetadata, UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, - VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, + GroupAssignedMetadata, GroupMetadata, GroupsBulkAssignedMetadata, MfaLoginMetadata, + MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, + NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, OpenIdAppMetadata, + OpenIdAppStateChangedMetadata, OpenIdProviderMetadata, UserAddedMetadata, + UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, + VpnLocationMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -330,6 +331,31 @@ pub async fn run_event_logger( }) .ok(), ), + DefguardEvent::GroupsBulkAssigned { users, groups } => ( + EventType::GroupsBulkAssigned, + serde_json::to_value(GroupsBulkAssignedMetadata { users, groups }) + .ok(), + ), + DefguardEvent::GroupAdded { group } => ( + EventType::GroupAdded, + serde_json::to_value(GroupMetadata { group }).ok(), + ), + DefguardEvent::GroupModified { group } => ( + EventType::GroupModified, + serde_json::to_value(GroupMetadata { group }).ok(), + ), + DefguardEvent::GroupRemoved { group } => ( + EventType::GroupRemoved, + serde_json::to_value(GroupMetadata { group }).ok(), + ), + DefguardEvent::GroupMemberAdded { group, user } => ( + EventType::GroupMemberAdded, + serde_json::to_value(GroupAssignedMetadata { group, user }).ok(), + ), + DefguardEvent::GroupMemberRemoved { group, user } => ( + EventType::GroupMemberRemoved, + serde_json::to_value(GroupAssignedMetadata { group, user }).ok(), + ), }; (module, event_type, metadata) } diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 296a10d1c..4aded4479 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -3,7 +3,7 @@ use std::net::IpAddr; use defguard_core::{ db::{ - models::authentication_key::AuthenticationKeyType, Device, Id, MFAMethod, User, + models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, WireguardNetwork, }, events::{ApiRequestContext, BidiRequestContext, GrpcRequestContext, InternalEventContext}, @@ -247,6 +247,28 @@ pub enum DefguardEvent { stream_id: Id, stream_name: String, }, + // groups management + GroupsBulkAssigned { + users: Vec>, + groups: Vec>, + }, + GroupAdded { + group: Group, + }, + GroupModified { + group: Group, + }, + GroupRemoved { + group: Group, + }, + GroupMemberAdded { + group: Group, + user: User, + }, + GroupMemberRemoved { + group: Group, + user: User, + }, } /// Represents audit events related to client applications diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 047564e01..58c2e53cd 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -198,6 +198,24 @@ impl EventRouter { ApiEventType::SettingsDefaultBrandingRestored => { LoggerEvent::Defguard(DefguardEvent::SettingsDefaultBrandingRestored) } + ApiEventType::GroupsBulkAssigned { users, groups } => { + LoggerEvent::Defguard(DefguardEvent::GroupsBulkAssigned { users, groups }) + } + ApiEventType::GroupAdded { group } => { + LoggerEvent::Defguard(DefguardEvent::GroupAdded { group }) + } + ApiEventType::GroupModified { group } => { + LoggerEvent::Defguard(DefguardEvent::GroupModified { group }) + } + ApiEventType::GroupRemoved { group } => { + LoggerEvent::Defguard(DefguardEvent::GroupRemoved { group }) + } + ApiEventType::GroupMemberAdded { group, user } => { + LoggerEvent::Defguard(DefguardEvent::GroupMemberAdded { group, user }) + } + ApiEventType::GroupMemberRemoved { group, user } => { + LoggerEvent::Defguard(DefguardEvent::GroupMemberRemoved { group, user }) + } }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index dc0e80959..ec8171e45 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2596,6 +2596,12 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T settings_updated: 'Settings updated', settings_updated_partial: 'Settings partially updated', settings_default_branding_restored: 'Default branding restored', + groups_bulk_assigned: 'Groups bulk assigned', + group_added: 'Group added', + group_modified: 'Group modified', + group_removed: 'Group removed', + group_member_added: 'Group member added', + group_member_removed: 'Group member removed', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 7a71f9573..f9d0cc282 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6298,6 +6298,30 @@ type RootTranslation = { * D​e​f​a​u​l​t​ ​b​r​a​n​d​i​n​g​ ​r​e​s​t​o​r​e​d */ settings_default_branding_restored: string + /** + * G​r​o​u​p​s​ ​b​u​l​k​ ​a​s​s​i​g​n​e​d + */ + groups_bulk_assigned: string + /** + * G​r​o​u​p​ ​a​d​d​e​d + */ + group_added: string + /** + * G​r​o​u​p​ ​m​o​d​i​f​i​e​d + */ + group_modified: string + /** + * G​r​o​u​p​ ​r​e​m​o​v​e​d + */ + group_removed: string + /** + * G​r​o​u​p​ ​m​e​m​b​e​r​ ​a​d​d​e​d + */ + group_member_added: string + /** + * G​r​o​u​p​ ​m​e​m​b​e​r​ ​r​e​m​o​v​e​d + */ + group_member_removed: string } auditModule: { /** @@ -12543,6 +12567,30 @@ export type TranslationFunctions = { * Default branding restored */ settings_default_branding_restored: () => LocalizedString + /** + * Groups bulk assigned + */ + groups_bulk_assigned: () => LocalizedString + /** + * Group added + */ + group_added: () => LocalizedString + /** + * Group modified + */ + group_modified: () => LocalizedString + /** + * Group removed + */ + group_removed: () => LocalizedString + /** + * Group member added + */ + group_member_added: () => LocalizedString + /** + * Group member removed + */ + group_member_removed: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 04534ed2a..9fbbaf1d9 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -58,7 +58,13 @@ export type AuditEventType = | 'open_id_provider_modified' | 'settings_updated' | 'settings_updated_partial' - | 'settings_default_branding_restored'; + | 'settings_default_branding_restored' + | 'groups_bulk_assigned' + | 'group_added' + | 'group_modified' + | 'group_removed' + | 'group_member_added' + | 'group_member_removed'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -112,4 +118,10 @@ export const auditEventTypeValues: AuditEventType[] = [ 'settings_updated', 'settings_updated_partial', 'settings_default_branding_restored', + 'groups_bulk_assigned', + 'group_added', + 'group_modified', + 'group_removed', + 'group_member_added', + 'group_member_removed', ]; From b0d3923f5db843a1f537a765ff1fdb01d0ed2b49 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Jun 2025 11:21:33 +0200 Subject: [PATCH 09/23] WebHook events --- .../src/db/models/audit_log/metadata.rs | 13 +++++- .../src/db/models/audit_log/mod.rs | 5 ++ crates/defguard_core/src/db/models/webhook.rs | 2 +- crates/defguard_core/src/events.rs | 15 +++++- crates/defguard_core/src/handlers/webhooks.rs | 46 +++++++++++++++---- crates/defguard_event_logger/src/lib.rs | 22 ++++++++- crates/defguard_event_logger/src/message.rs | 15 +++++- .../defguard_event_router/src/handlers/api.rs | 12 +++++ web/src/i18n/en/index.ts | 15 +++--- web/src/i18n/i18n-types.ts | 24 ++++++++++ web/src/pages/activity/types.ts | 8 +++- 11 files changed, 155 insertions(+), 22 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index f23c9d293..2c6f19442 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -1,4 +1,4 @@ -use crate::db::{Device, Group, Id, MFAMethod, User, WireguardNetwork}; +use crate::db::{Device, Group, Id, MFAMethod, User, WebHook, WireguardNetwork}; #[derive(Serialize)] pub struct MfaLoginMetadata { @@ -148,3 +148,14 @@ pub struct GroupAssignedMetadata { pub group: Group, pub user: User, } + +#[derive(Serialize)] +pub struct WebHookMetadata { + pub webhook: WebHook, +} + +#[derive(Serialize)] +pub struct WebHookStateChangedMetadata { + pub webhook: WebHook, + pub enabled: bool, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index 1181dd254..85b995128 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -94,6 +94,11 @@ pub enum EventType { GroupRemoved, GroupMemberAdded, GroupMemberRemoved, + // WebHook management + WebHookAdded, + WebHookModified, + WebHookRemoved, + WebHookStateChanged, } #[derive(Model, FromRow, Serialize)] diff --git a/crates/defguard_core/src/db/models/webhook.rs b/crates/defguard_core/src/db/models/webhook.rs index c9eda286c..5263c6980 100644 --- a/crates/defguard_core/src/db/models/webhook.rs +++ b/crates/defguard_core/src/db/models/webhook.rs @@ -46,7 +46,7 @@ impl AppEvent { } } -#[derive(Debug, Deserialize, FromRow, Model, Serialize)] +#[derive(Clone, Debug, Deserialize, FromRow, Model, Serialize)] pub struct WebHook { pub id: I, pub url: String, diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 094335741..f538a82c9 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -1,6 +1,6 @@ use std::net::IpAddr; -use crate::db::{Device, Group, Id, MFAMethod, User, WireguardNetwork}; +use crate::db::{Device, Group, Id, MFAMethod, User, WebHook, WireguardNetwork}; use chrono::{NaiveDateTime, Utc}; /// Shared context that needs to be added to every API event @@ -213,6 +213,19 @@ pub enum ApiEventType { group: Group, user: User, }, + WebHookAdded { + webhook: WebHook, + }, + WebHookModified { + webhook: WebHook, + }, + WebHookRemoved { + webhook: WebHook, + }, + WebHookStateChanged { + webhook: WebHook, + enabled: bool, + }, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/webhooks.rs b/crates/defguard_core/src/handlers/webhooks.rs index cc6513e36..c3aeb353f 100644 --- a/crates/defguard_core/src/handlers/webhooks.rs +++ b/crates/defguard_core/src/handlers/webhooks.rs @@ -9,11 +9,13 @@ use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, db::WebHook, + events::{ApiEvent, ApiEventType, ApiRequestContext}, }; pub async fn add_webhook( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Json(webhookdata): Json, ) -> ApiResult { @@ -21,10 +23,16 @@ pub async fn add_webhook( debug!("User {} adding webhook {url}", session.user.username); let webhook: WebHook = webhookdata.into(); let status = match webhook.save(&appstate.pool).await { - Ok(_) => StatusCode::CREATED, + Ok(webhook) => { + info!("User {} added webhook {url}", session.user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::WebHookAdded { webhook }, + })?; + StatusCode::CREATED + } Err(_) => StatusCode::BAD_REQUEST, }; - info!("User {} added webhook {url}", session.user.username); Ok(ApiResponse { json: json!({}), @@ -62,6 +70,7 @@ pub async fn get_webhook( pub async fn change_webhook( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(id): Path, Json(data): Json, @@ -78,11 +87,15 @@ pub async fn change_webhook( webhook.on_user_modified = data.on_user_modified; webhook.on_hwkey_provision = data.on_hwkey_provision; webhook.save(&appstate.pool).await?; + info!("User {} updated webhook {id}", session.user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::WebHookModified { webhook }, + })?; StatusCode::OK } None => StatusCode::NOT_FOUND, }; - info!("User {} updated webhook {id}", session.user.username); Ok(ApiResponse { json: json!({}), @@ -93,18 +106,23 @@ pub async fn change_webhook( pub async fn delete_webhook( _admin: AdminRole, State(appstate): State, - Path(id): Path, session: SessionInfo, + context: ApiRequestContext, + Path(id): Path, ) -> ApiResult { debug!("User {} deleting webhook {id}", session.user.username); let status = match WebHook::find_by_id(&appstate.pool, id).await? { Some(webhook) => { - webhook.delete(&appstate.pool).await?; + webhook.clone().delete(&appstate.pool).await?; + info!("User {} deleted webhook {id}", session.user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::WebHookRemoved { webhook }, + })?; StatusCode::OK } None => StatusCode::NOT_FOUND, }; - info!("User {} deleted webhook {id}", session.user.username); Ok(ApiResponse { json: json!({}), status, @@ -119,6 +137,7 @@ pub struct ChangeStateData { pub async fn change_enabled( _admin: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(id): Path, Json(data): Json, @@ -131,14 +150,21 @@ pub async fn change_enabled( Some(mut webhook) => { webhook.enabled = data.enabled; webhook.save(&appstate.pool).await?; + info!( + "User {} changed webhook {id} enabled state to {}", + session.user.username, data.enabled + ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::WebHookStateChanged { + enabled: webhook.enabled, + webhook, + }, + })?; StatusCode::OK } None => StatusCode::NOT_FOUND, }; - info!( - "User {} changed webhook {id} enabled state to {}", - session.user.username, data.enabled - ); Ok(ApiResponse { json: json!({}), status, diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 5366021f7..1eaed7aa9 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -17,7 +17,7 @@ use defguard_core::db::{ NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, OpenIdAppMetadata, OpenIdAppStateChangedMetadata, OpenIdProviderMetadata, UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, - VpnLocationMetadata, + VpnLocationMetadata, WebHookMetadata, WebHookStateChangedMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -356,6 +356,26 @@ pub async fn run_event_logger( EventType::GroupMemberRemoved, serde_json::to_value(GroupAssignedMetadata { group, user }).ok(), ), + DefguardEvent::WebHookAdded { webhook } => ( + EventType::WebHookAdded, + serde_json::to_value(WebHookMetadata { webhook }).ok(), + ), + DefguardEvent::WebHookModified { webhook } => ( + EventType::WebHookModified, + serde_json::to_value(WebHookMetadata { webhook }).ok(), + ), + DefguardEvent::WebHookRemoved { webhook } => ( + EventType::WebHookRemoved, + serde_json::to_value(WebHookMetadata { webhook }).ok(), + ), + DefguardEvent::WebHookStateChanged { webhook, enabled } => ( + EventType::WebHookStateChanged, + serde_json::to_value(WebHookStateChangedMetadata { + webhook, + enabled, + }) + .ok(), + ), }; (module, event_type, metadata) } diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 4aded4479..5fea1f9ce 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -4,7 +4,7 @@ use std::net::IpAddr; use defguard_core::{ db::{ models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, - WireguardNetwork, + WebHook, WireguardNetwork, }, events::{ApiRequestContext, BidiRequestContext, GrpcRequestContext, InternalEventContext}, }; @@ -269,6 +269,19 @@ pub enum DefguardEvent { group: Group, user: User, }, + WebHookAdded { + webhook: WebHook, + }, + WebHookModified { + webhook: WebHook, + }, + WebHookRemoved { + webhook: WebHook, + }, + WebHookStateChanged { + webhook: WebHook, + enabled: bool, + }, } /// Represents audit events related to client applications diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 58c2e53cd..1c8360c9e 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -216,6 +216,18 @@ impl EventRouter { ApiEventType::GroupMemberRemoved { group, user } => { LoggerEvent::Defguard(DefguardEvent::GroupMemberRemoved { group, user }) } + ApiEventType::WebHookAdded { webhook } => { + LoggerEvent::Defguard(DefguardEvent::WebHookAdded { webhook }) + } + ApiEventType::WebHookModified { webhook } => { + LoggerEvent::Defguard(DefguardEvent::WebHookModified { webhook }) + } + ApiEventType::WebHookRemoved { webhook } => { + LoggerEvent::Defguard(DefguardEvent::WebHookRemoved { webhook }) + } + ApiEventType::WebHookStateChanged { webhook, enabled } => { + LoggerEvent::Defguard(DefguardEvent::WebHookStateChanged { webhook, enabled }) + } }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index ec8171e45..c08806886 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2596,12 +2596,15 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T settings_updated: 'Settings updated', settings_updated_partial: 'Settings partially updated', settings_default_branding_restored: 'Default branding restored', - groups_bulk_assigned: 'Groups bulk assigned', - group_added: 'Group added', - group_modified: 'Group modified', - group_removed: 'Group removed', - group_member_added: 'Group member added', - group_member_removed: 'Group member removed', + groups_bulk_assigned: 'Groups bulk assigned', + group_added: 'Group added', + group_modified: 'Group modified', + group_removed: 'Group removed', + group_member_added: 'Group member added', + group_member_removed: 'Group member removed', + web_hook_added: 'Webhook added', + web_hook_modified: 'Webhook modified', + web_hook_removed: 'Webhook removed', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index f9d0cc282..e93f78c3f 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6322,6 +6322,18 @@ type RootTranslation = { * G​r​o​u​p​ ​m​e​m​b​e​r​ ​r​e​m​o​v​e​d */ group_member_removed: string + /** + * W​e​b​h​o​o​k​ ​a​d​d​e​d + */ + web_hook_added: string + /** + * W​e​b​h​o​o​k​ ​m​o​d​i​f​i​e​d + */ + web_hook_modified: string + /** + * W​e​b​h​o​o​k​ ​r​e​m​o​v​e​d + */ + web_hook_removed: string } auditModule: { /** @@ -12591,6 +12603,18 @@ export type TranslationFunctions = { * Group member removed */ group_member_removed: () => LocalizedString + /** + * Webhook added + */ + web_hook_added: () => LocalizedString + /** + * Webhook modified + */ + web_hook_modified: () => LocalizedString + /** + * Webhook removed + */ + web_hook_removed: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 9fbbaf1d9..6c78b391f 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -64,7 +64,10 @@ export type AuditEventType = | 'group_modified' | 'group_removed' | 'group_member_added' - | 'group_member_removed'; + | 'group_member_removed' + | 'web_hook_added' + | 'web_hook_modified' + | 'web_hook_removed' export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -124,4 +127,7 @@ export const auditEventTypeValues: AuditEventType[] = [ 'group_removed', 'group_member_added', 'group_member_removed', + 'web_hook_added', + 'web_hook_modified', + 'web_hook_removed', ]; From 22c59244836ee05f0ae33729186c870f7765343c Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Jun 2025 13:16:17 +0200 Subject: [PATCH 10/23] Webauthn key management events --- crates/defguard_core/src/db/models/webauthn.rs | 4 ++-- crates/defguard_core/src/handlers/auth.rs | 13 +++++++++++-- crates/defguard_core/src/handlers/user.rs | 7 +++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/defguard_core/src/db/models/webauthn.rs b/crates/defguard_core/src/db/models/webauthn.rs index 29fbe4695..cd5a0879a 100644 --- a/crates/defguard_core/src/db/models/webauthn.rs +++ b/crates/defguard_core/src/db/models/webauthn.rs @@ -7,9 +7,9 @@ use crate::db::{Id, NoId}; #[derive(Model)] pub struct WebAuthn { - id: I, + pub(crate) id: I, pub(crate) user_id: Id, - name: String, + pub(crate) name: String, // serialize from/to [`Passkey`] pub passkey: Vec, } diff --git a/crates/defguard_core/src/handlers/auth.rs b/crates/defguard_core/src/handlers/auth.rs index 413e4ffca..e681e0110 100644 --- a/crates/defguard_core/src/handlers/auth.rs +++ b/crates/defguard_core/src/handlers/auth.rs @@ -442,6 +442,7 @@ pub async fn webauthn_init( /// Finish WebAuthn registration pub async fn webauthn_finish( session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Json(webauth_reg): Json, ) -> ApiResult { @@ -485,8 +486,9 @@ pub async fn webauthn_finish( .await? .ok_or(WebError::WebauthnRegistration("User not found".into()))?; let recovery_codes = RecoveryCodes::new(user.get_recovery_codes(&appstate.pool).await?); - let webauthn = WebAuthn::new(session.session.user_id, webauth_reg.name, &passkey)?; - webauthn.save(&appstate.pool).await?; + let webauthn = WebAuthn::new(session.session.user_id, webauth_reg.name, &passkey)? + .save(&appstate.pool) + .await?; if user.mfa_method == MFAMethod::None { send_mfa_configured_email( Some(&session.session), @@ -499,6 +501,13 @@ pub async fn webauthn_finish( } info!("Finished Webauthn registration for user {}", user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::MfaSecurityKeyAdded { + key_id: webauthn.id, + key_name: webauthn.name, + }, + })?; Ok(ApiResponse { json: json!(recovery_codes), diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index 86fdd5bdc..e61bd2f44 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -1095,6 +1095,7 @@ pub async fn reset_password( )] pub async fn delete_security_key( session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path((username, id)): Path<(String, i64)>, ) -> ApiResult { @@ -1105,12 +1106,18 @@ pub async fn delete_security_key( let mut user = user_for_admin_or_self(&appstate.pool, &session, &username).await?; if let Some(webauthn) = WebAuthn::find_by_id(&appstate.pool, id).await? { if webauthn.user_id == user.id { + let key_id = webauthn.id; + let key_name = webauthn.name.clone(); webauthn.delete(&appstate.pool).await?; user.verify_mfa_state(&appstate.pool).await?; info!( "User {} deleted security key {id} for user {username}", session.user.username, ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::MfaSecurityKeyRemoved { key_id, key_name }, + })?; Ok(ApiResponse::default()) } else { error!( From 2fe02668e0b789d361074a90285f55871ced90b0 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 17 Jun 2025 08:10:14 +0200 Subject: [PATCH 11/23] AuthenticationKey events --- .../src/db/models/audit_log/metadata.rs | 20 ++++++- .../src/db/models/audit_log/mod.rs | 4 ++ .../src/db/models/authentication_key.rs | 12 ++--- crates/defguard_core/src/events.rs | 21 +++++++- .../src/handlers/ssh_authorized_keys.rs | 42 ++++++++++++++- crates/defguard_event_logger/src/lib.rs | 53 ++++++++++++++----- crates/defguard_event_logger/src/message.rs | 7 +-- .../defguard_event_router/src/handlers/api.rs | 29 ++++++++++ web/src/i18n/en/index.ts | 3 ++ web/src/i18n/i18n-types.ts | 24 +++++++++ web/src/pages/activity/types.ts | 6 +++ 11 files changed, 195 insertions(+), 26 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 2c6f19442..ee7f168ae 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -1,4 +1,7 @@ -use crate::db::{Device, Group, Id, MFAMethod, User, WebHook, WireguardNetwork}; +use crate::db::{ + models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, WebHook, + WireguardNetwork, +}; #[derive(Serialize)] pub struct MfaLoginMetadata { @@ -159,3 +162,18 @@ pub struct WebHookStateChangedMetadata { pub webhook: WebHook, pub enabled: bool, } + +#[derive(Serialize)] +pub struct AuthenticationKeyMetadata { + pub key_id: Id, + pub key_name: Option, + pub key_type: AuthenticationKeyType, +} + +#[derive(Serialize)] +pub struct AuthenticationKeyRenamedMetadata { + pub key_id: Id, + pub key_type: AuthenticationKeyType, + pub old_name: Option, + pub new_name: Option, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index 85b995128..26b63096a 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -99,6 +99,10 @@ pub enum EventType { WebHookModified, WebHookRemoved, WebHookStateChanged, + // Authentication key management + AuthenticationKeyAdded, + AuthenticationKeyRemoved, + AuthenticationKeyRenamed, } #[derive(Model, FromRow, Serialize)] diff --git a/crates/defguard_core/src/db/models/authentication_key.rs b/crates/defguard_core/src/db/models/authentication_key.rs index cf877ca70..a61b53a39 100644 --- a/crates/defguard_core/src/db/models/authentication_key.rs +++ b/crates/defguard_core/src/db/models/authentication_key.rs @@ -14,13 +14,13 @@ pub enum AuthenticationKeyType { #[derive(Deserialize, Model, Serialize)] #[table(authentication_key)] pub(crate) struct AuthenticationKey { - id: I, - pub yubikey_id: Option, - pub name: Option, - pub user_id: Id, - pub key: String, + pub(crate) id: I, + pub(crate) yubikey_id: Option, + pub(crate) name: Option, + pub(crate) user_id: Id, + pub(crate) key: String, #[model(enum)] - key_type: AuthenticationKeyType, + pub(crate) key_type: AuthenticationKeyType, } impl AuthenticationKey { diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index f538a82c9..67390caee 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -1,6 +1,9 @@ use std::net::IpAddr; -use crate::db::{Device, Group, Id, MFAMethod, User, WebHook, WireguardNetwork}; +use crate::db::{ + models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, WebHook, + WireguardNetwork, +}; use chrono::{NaiveDateTime, Utc}; /// Shared context that needs to be added to every API event @@ -226,6 +229,22 @@ pub enum ApiEventType { webhook: WebHook, enabled: bool, }, + AuthenticationKeyAdded { + key_id: Id, + key_name: Option, + key_type: AuthenticationKeyType, + }, + AuthenticationKeyRemoved { + key_id: Id, + key_name: Option, + key_type: AuthenticationKeyType, + }, + AuthenticationKeyRenamed { + key_id: Id, + key_type: AuthenticationKeyType, + old_name: Option, + new_name: Option, + }, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/ssh_authorized_keys.rs b/crates/defguard_core/src/handlers/ssh_authorized_keys.rs index ef8075965..8975d2434 100644 --- a/crates/defguard_core/src/handlers/ssh_authorized_keys.rs +++ b/crates/defguard_core/src/handlers/ssh_authorized_keys.rs @@ -16,6 +16,7 @@ use crate::{ Group, Id, User, }, error::WebError, + events::{ApiEvent, ApiEventType, ApiRequestContext}, }; #[derive(Deserialize, Serialize)] @@ -156,6 +157,7 @@ pub struct AddAuthenticationKeyData { pub async fn add_authentication_key( State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Path(username): Path, Json(data): Json, ) -> ApiResult { @@ -195,7 +197,7 @@ pub async fn add_authentication_key( return Err(WebError::BadRequest("Key already exists.".into())); } - AuthenticationKey::new( + let key = AuthenticationKey::new( user.id, trimmed_key.to_string(), Some(data.name.clone()), @@ -209,6 +211,14 @@ pub async fn add_authentication_key( "Added new key \"{}\" of type {:?} for user {username}", data.name, data.key_type ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::AuthenticationKeyAdded { + key_id: key.id, + key_name: key.name, + key_type: key.key_type, + }, + })?; Ok(ApiResponse { json: json!({}), @@ -234,6 +244,7 @@ pub async fn fetch_authentication_keys( pub async fn delete_authentication_key( State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Path((username, key_id)): Path<(String, i64)>, ) -> ApiResult { let user = user_for_admin_or_self(&appstate.pool, &session, &username).await?; @@ -241,7 +252,19 @@ pub async fn delete_authentication_key( if !session.is_admin && user.id != key.user_id { return Err(WebError::Forbidden(String::new())); } - key.delete(&appstate.pool).await?; + + info!( + "Removed key \"{:?}\"({}) of type {:?} for user {username}", + key.name, key.id, key.key_type + ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::AuthenticationKeyRemoved { + key_id: key.id, + key_name: key.name, + key_type: key.key_type, + }, + })?; } else { error!("Key with id {} not found", key_id); return Err(WebError::BadRequest("Key not found".into())); @@ -261,6 +284,7 @@ pub struct RenameRequest { pub async fn rename_authentication_key( State(appstate): State, session: SessionInfo, + context: ApiRequestContext, Path((username, key_id)): Path<(String, i64)>, Json(data): Json, ) -> ApiResult { @@ -280,8 +304,22 @@ pub async fn rename_authentication_key( ); return Err(WebError::Forbidden(String::new())); } + let old_name = key.name.clone(); key.name = Some(data.name); key.save(&appstate.pool).await?; + info!( + "User {} renamed key {:?}({}) of user with id {}", + user.username, key.name, key.id, key.user_id + ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::AuthenticationKeyRenamed { + key_id: key.id, + old_name, + new_name: key.name, + key_type: key.key_type, + }, + })?; } else { error!( "User {} tried to rename non-existing key with id {}", diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 1eaed7aa9..0bf391d81 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -10,7 +10,8 @@ use tracing::{debug, error, info, trace}; use defguard_core::db::{ models::audit_log::{ metadata::{ - ApiTokenMetadata, ApiTokenRenamedMetadata, AuditStreamMetadata, DeviceAddedMetadata, + ApiTokenMetadata, ApiTokenRenamedMetadata, AuditStreamMetadata, + AuthenticationKeyMetadata, AuthenticationKeyRenamedMetadata, DeviceAddedMetadata, DeviceModifiedMetadata, DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, GroupAssignedMetadata, GroupMetadata, GroupsBulkAssignedMetadata, MfaLoginMetadata, MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, @@ -139,20 +140,46 @@ pub async fn run_event_logger( .ok(), ), DefguardEvent::AuthenticationKeyAdded { - key_id: _, - key_name: _, - key_type: _, - } => todo!(), + key_id, + key_name, + key_type, + } => ( + EventType::AuthenticationKeyAdded, + serde_json::to_value(AuthenticationKeyMetadata { + key_id, + key_name, + key_type, + }) + .ok(), + ), DefguardEvent::AuthenticationKeyRemoved { - key_id: _, - key_name: _, - key_type: _, - } => todo!(), + key_id, + key_name, + key_type, + } => ( + EventType::AuthenticationKeyRemoved, + serde_json::to_value(AuthenticationKeyMetadata { + key_id, + key_name, + key_type, + }) + .ok(), + ), DefguardEvent::AuthenticationKeyRenamed { - key_id: _, - key_name: _, - key_type: _, - } => todo!(), + key_id, + old_name, + new_name, + key_type, + } => ( + EventType::AuthenticationKeyRenamed, + serde_json::to_value(AuthenticationKeyRenamedMetadata { + key_id, + old_name, + new_name, + key_type, + }) + .ok(), + ), DefguardEvent::ApiTokenAdded { owner, token_name } => ( EventType::ApiTokenAdded, serde_json::to_value(ApiTokenMetadata { owner, token_name }).ok(), diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 5fea1f9ce..8f16c93a6 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -119,18 +119,19 @@ pub enum DefguardEvent { // authentication key management AuthenticationKeyAdded { key_id: Id, - key_name: String, + key_name: Option, key_type: AuthenticationKeyType, }, AuthenticationKeyRemoved { key_id: Id, - key_name: String, + key_name: Option, key_type: AuthenticationKeyType, }, AuthenticationKeyRenamed { key_id: Id, - key_name: String, key_type: AuthenticationKeyType, + old_name: Option, + new_name: Option, }, // API token management ApiTokenAdded { diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 1c8360c9e..cd450e87e 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -228,6 +228,35 @@ impl EventRouter { ApiEventType::WebHookStateChanged { webhook, enabled } => { LoggerEvent::Defguard(DefguardEvent::WebHookStateChanged { webhook, enabled }) } + ApiEventType::AuthenticationKeyAdded { + key_id, + key_name, + key_type, + } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyAdded { + key_id, + key_name, + key_type, + }), + ApiEventType::AuthenticationKeyRemoved { + key_id, + key_name, + key_type, + } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRemoved { + key_id, + key_name, + key_type, + }), + ApiEventType::AuthenticationKeyRenamed { + key_id, + key_type, + old_name, + new_name, + } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRenamed { + key_id, + key_type, + old_name, + new_name, + }), }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index c08806886..644d35541 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2605,6 +2605,9 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T web_hook_added: 'Webhook added', web_hook_modified: 'Webhook modified', web_hook_removed: 'Webhook removed', + authentication_key_added: 'Authentication key added', + authentication_key_removed: 'Authentication key removed', + authentication_key_renamed: 'Authentication key renamed', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index e93f78c3f..1c7decbb2 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6334,6 +6334,18 @@ type RootTranslation = { * W​e​b​h​o​o​k​ ​r​e​m​o​v​e​d */ web_hook_removed: string + /** + * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​a​d​d​e​d + */ + authentication_key_added: string + /** + * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​r​e​m​o​v​e​d + */ + authentication_key_removed: string + /** + * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​r​e​n​a​m​e​d + */ + authentication_key_renamed: string } auditModule: { /** @@ -12615,6 +12627,18 @@ export type TranslationFunctions = { * Webhook removed */ web_hook_removed: () => LocalizedString + /** + * Authentication key added + */ + authentication_key_added: () => LocalizedString + /** + * Authentication key removed + */ + authentication_key_removed: () => LocalizedString + /** + * Authentication key renamed + */ + authentication_key_renamed: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 6c78b391f..3f9d5e29c 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -68,6 +68,9 @@ export type AuditEventType = | 'web_hook_added' | 'web_hook_modified' | 'web_hook_removed' + | 'authentication_key_added' + | 'authentication_key_removed' + | 'authentication_key_renamed' export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -130,4 +133,7 @@ export const auditEventTypeValues: AuditEventType[] = [ 'web_hook_added', 'web_hook_modified', 'web_hook_removed', + 'authentication_key_added', + 'authentication_key_removed', + 'authentication_key_renamed', ]; From 31d34310a24ed9e30584704663a88d66fbd4f3ff Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 17 Jun 2025 13:07:07 +0200 Subject: [PATCH 12/23] Password change, enrollment events --- .../src/db/models/audit_log/metadata.rs | 20 ++++++++++ .../src/db/models/audit_log/mod.rs | 5 +++ crates/defguard_core/src/events.rs | 13 ++++++ crates/defguard_core/src/handlers/user.rs | 29 +++++++++++++- crates/defguard_event_logger/src/lib.rs | 33 ++++++++++++--- crates/defguard_event_logger/src/message.rs | 10 +++++ .../defguard_event_router/src/handlers/api.rs | 15 ++++++- web/src/i18n/en/index.ts | 5 +++ web/src/i18n/i18n-types.ts | 40 +++++++++++++++++++ web/src/pages/activity/types.ts | 10 +++++ 10 files changed, 171 insertions(+), 9 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index ee7f168ae..d5a811edc 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -98,6 +98,11 @@ pub struct EnrollmentDeviceAddedMetadata { pub device: Device, } +#[derive(Serialize)] +pub struct EnrollmentTokenAddedMetadata { + pub user: User, +} + #[derive(Serialize)] pub struct VpnLocationMetadata { pub location: WireguardNetwork, @@ -177,3 +182,18 @@ pub struct AuthenticationKeyRenamedMetadata { pub old_name: Option, pub new_name: Option, } + +#[derive(Serialize)] +pub struct PasswordChangedByAdminMetadata { + pub user: User, +} + +#[derive(Serialize)] +pub struct PasswordResetMetadata { + pub user: User, +} + +#[derive(Serialize)] +pub struct ClientConfigurationTokenAddedMetadata { + pub user: User, +} diff --git a/crates/defguard_core/src/db/models/audit_log/mod.rs b/crates/defguard_core/src/db/models/audit_log/mod.rs index 26b63096a..0cf327d92 100644 --- a/crates/defguard_core/src/db/models/audit_log/mod.rs +++ b/crates/defguard_core/src/db/models/audit_log/mod.rs @@ -43,6 +43,9 @@ pub enum EventType { UserAdded, UserRemoved, UserModified, + PasswordChanged, + PasswordChangedByAdmin, + PasswordReset, // device management DeviceAdded, DeviceRemoved, @@ -50,6 +53,7 @@ pub enum EventType { NetworkDeviceAdded, NetworkDeviceRemoved, NetworkDeviceModified, + ClientConfigurationTokenAdded, // audit stream AuditStreamCreated, AuditStreamModified, @@ -73,6 +77,7 @@ pub enum EventType { VpnClientDisconnectedMfa, VpnClientMfaFailed, // Enrollment events + EnrollmentTokenAdded, EnrollmentStarted, EnrollmentDeviceAdded, EnrollmentCompleted, diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 67390caee..4a33f8aad 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -245,6 +245,19 @@ pub enum ApiEventType { old_name: Option, new_name: Option, }, + PasswordChangedByAdmin { + user: User, + }, + PasswordChanged, + PasswordReset { + user: User, + }, + EnrollmentTokenAdded { + user: User, + }, + ClientConfigurationTokenAdded { + user: User, + }, } /// Events from Web API diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index e61bd2f44..8f34a926c 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -383,12 +383,13 @@ pub async fn add_user( pub async fn start_enrollment( _role: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(username): Path, Json(data): Json, ) -> ApiResult { debug!( - "User {} has started a new enrollment request.", + "User {} creating enrollment token for user {username}.", session.user.username ); @@ -435,7 +436,7 @@ pub async fn start_enrollment( debug!("Transaction committed."); info!( - "The enrollment process for {} has ended with success.", + "User {} created enrollment token for user {username}.", session.user.username ); debug!( @@ -443,6 +444,10 @@ pub async fn start_enrollment( enrollment_token, config.enrollment_url.to_string() ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::EnrollmentTokenAdded { user }, + })?; Ok(ApiResponse { json: json!({"enrollment_token": enrollment_token, "enrollment_url": config.enrollment_url.to_string()}), @@ -479,6 +484,7 @@ pub async fn start_enrollment( )] pub async fn start_remote_desktop_configuration( session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(username): Path, Json(data): Json, @@ -539,6 +545,10 @@ pub async fn start_remote_desktop_configuration( desktop_configuration_token, config.enrollment_url.to_string() ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::ClientConfigurationTokenAdded { user }, + })?; Ok(ApiResponse { json: json!({"enrollment_token": desktop_configuration_token, "enrollment_url": config.enrollment_url.to_string()}), @@ -843,6 +853,7 @@ pub async fn delete_user( )] pub async fn change_self_password( session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Json(data): Json, ) -> ApiResult { @@ -869,6 +880,10 @@ pub async fn change_self_password( ldap_change_password(&mut user, &data.new_password, &appstate.pool).await; info!("User {} changed his password.", &user.username); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::PasswordChanged, + })?; Ok(ApiResponse { json: json!({}), @@ -907,6 +922,7 @@ pub async fn change_self_password( pub async fn change_password( _role: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(username): Path, Json(data): Json, @@ -949,6 +965,10 @@ pub async fn change_password( "Admin {} changed password for user {username}", session.user.username ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::PasswordChangedByAdmin { user }, + })?; Ok(ApiResponse::default()) } else { debug!("Can't change password for user {username}, user not found"); @@ -989,6 +1009,7 @@ pub async fn change_password( pub async fn reset_password( _role: AdminRole, session: SessionInfo, + context: ApiRequestContext, State(appstate): State, Path(username): Path, ) -> ApiResult { @@ -1058,6 +1079,10 @@ pub async fn reset_password( "Admin {} reset password for user {username}", session.user.username ); + appstate.emit_event(ApiEvent { + context, + event: ApiEventType::PasswordReset { user }, + })?; Ok(ApiResponse::default()) } else { debug!("Can't reset password for user {username}, user not found"); diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 0bf391d81..928274689 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -11,14 +11,16 @@ use defguard_core::db::{ models::audit_log::{ metadata::{ ApiTokenMetadata, ApiTokenRenamedMetadata, AuditStreamMetadata, - AuthenticationKeyMetadata, AuthenticationKeyRenamedMetadata, DeviceAddedMetadata, - DeviceModifiedMetadata, DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, + AuthenticationKeyMetadata, AuthenticationKeyRenamedMetadata, + ClientConfigurationTokenAddedMetadata, DeviceAddedMetadata, DeviceModifiedMetadata, + DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, EnrollmentTokenAddedMetadata, GroupAssignedMetadata, GroupMetadata, GroupsBulkAssignedMetadata, MfaLoginMetadata, MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, OpenIdAppMetadata, - OpenIdAppStateChangedMetadata, OpenIdProviderMetadata, UserAddedMetadata, - UserModifiedMetadata, UserRemovedMetadata, VpnClientMetadata, VpnClientMfaMetadata, - VpnLocationMetadata, WebHookMetadata, WebHookStateChangedMetadata, + OpenIdAppStateChangedMetadata, OpenIdProviderMetadata, PasswordChangedByAdminMetadata, + PasswordResetMetadata, UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, + VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, WebHookMetadata, + WebHookStateChangedMetadata, }, AuditEvent, AuditModule, EventType, }, @@ -117,7 +119,11 @@ pub async fn run_event_logger( .ok(), ), DefguardEvent::RecoveryCodeUsed => (EventType::RecoveryCodeUsed, None), - DefguardEvent::PasswordChanged => todo!(), + DefguardEvent::PasswordChanged => (EventType::PasswordChanged, None), + DefguardEvent::PasswordChangedByAdmin { user } => ( + EventType::PasswordChangedByAdmin, + serde_json::to_value(PasswordChangedByAdminMetadata { user }).ok(), + ), DefguardEvent::MfaDisabled => (EventType::MfaDisabled, None), DefguardEvent::MfaTotpEnabled => (EventType::MfaTotpEnabled, None), DefguardEvent::MfaTotpDisabled => (EventType::MfaTotpDisabled, None), @@ -403,6 +409,17 @@ pub async fn run_event_logger( }) .ok(), ), + DefguardEvent::PasswordReset { user } => ( + EventType::PasswordReset, + serde_json::to_value(PasswordResetMetadata { user }).ok(), + ), + DefguardEvent::ClientConfigurationTokenAdded { user } => ( + EventType::ClientConfigurationTokenAdded, + serde_json::to_value(ClientConfigurationTokenAddedMetadata { + user, + }) + .ok(), + ), }; (module, event_type, metadata) } @@ -476,6 +493,10 @@ pub async fn run_event_logger( EnrollmentEvent::PasswordResetCompleted => { (EventType::PasswordResetCompleted, None) } + EnrollmentEvent::TokenAdded { user } => ( + EventType::EnrollmentTokenAdded, + serde_json::to_value(EnrollmentTokenAddedMetadata { user }).ok(), + ), }; (module, event_type, metadata) } diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 8f16c93a6..1f8769e59 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -101,7 +101,13 @@ pub enum DefguardEvent { }, UserLogout, RecoveryCodeUsed, + PasswordChangedByAdmin { + user: User, + }, PasswordChanged, + PasswordReset { + user: User, + }, // user MFA management MfaDisabled, MfaTotpEnabled, @@ -283,6 +289,9 @@ pub enum DefguardEvent { webhook: WebHook, enabled: bool, }, + ClientConfigurationTokenAdded { + user: User, + }, } /// Represents audit events related to client applications @@ -325,4 +334,5 @@ pub enum EnrollmentEvent { PasswordResetRequested, PasswordResetStarted, PasswordResetCompleted, + TokenAdded { user: User }, } diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index cd450e87e..949ea3415 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -1,5 +1,5 @@ use defguard_core::events::{ApiEvent, ApiEventType}; -use defguard_event_logger::message::{DefguardEvent, LoggerEvent}; +use defguard_event_logger::message::{DefguardEvent, EnrollmentEvent, LoggerEvent}; use tracing::debug; use crate::{error::EventRouterError, EventRouter}; @@ -257,6 +257,19 @@ impl EventRouter { old_name, new_name, }), + ApiEventType::EnrollmentTokenAdded { user } => { + LoggerEvent::Enrollment(EnrollmentEvent::TokenAdded { user }) + } + ApiEventType::PasswordChanged => LoggerEvent::Defguard(DefguardEvent::PasswordChanged), + ApiEventType::PasswordChangedByAdmin { user } => { + LoggerEvent::Defguard(DefguardEvent::PasswordChangedByAdmin { user }) + } + ApiEventType::PasswordReset { user } => { + LoggerEvent::Defguard(DefguardEvent::PasswordReset { user }) + } + ApiEventType::ClientConfigurationTokenAdded { user } => { + LoggerEvent::Defguard(DefguardEvent::ClientConfigurationTokenAdded { user }) + } }; self.log_event(event.context.into(), logger_event) } diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 644d35541..81ae294b9 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2575,6 +2575,7 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T vpn_client_connected_mfa: 'VPN client connected to MFA location', vpn_client_disconnected_mfa: 'VPN client disconnected from MFA location', vpn_client_mfa_failed: 'VPN client failed MFA authentication', + enrollment_token_added: 'Enrollment token added', enrollment_started: 'Enrollment started', enrollment_device_added: 'Device added', enrollment_completed: 'Enrollment completed', @@ -2608,6 +2609,10 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T authentication_key_added: 'Authentication key added', authentication_key_removed: 'Authentication key removed', authentication_key_renamed: 'Authentication key renamed', + password_changed: 'Password changed', + password_changed_by_admin: 'Password changed by admin', + password_reset: 'Password reset', + client_configuration_token_added: 'Client configuration token added', }, auditModule: { defguard: 'Defguard', diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 1c7decbb2..ee9e5c34c 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -6214,6 +6214,10 @@ type RootTranslation = { * V​P​N​ ​c​l​i​e​n​t​ ​f​a​i​l​e​d​ ​M​F​A​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n */ vpn_client_mfa_failed: string + /** + * E​n​r​o​l​l​m​e​n​t​ ​t​o​k​e​n​ ​a​d​d​e​d + */ + enrollment_token_added: string /** * E​n​r​o​l​l​m​e​n​t​ ​s​t​a​r​t​e​d */ @@ -6346,6 +6350,22 @@ type RootTranslation = { * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​r​e​n​a​m​e​d */ authentication_key_renamed: string + /** + * P​a​s​s​w​o​r​d​ ​c​h​a​n​g​e​d + */ + password_changed: string + /** + * P​a​s​s​w​o​r​d​ ​c​h​a​n​g​e​d​ ​b​y​ ​a​d​m​i​n + */ + password_changed_by_admin: string + /** + * P​a​s​s​w​o​r​d​ ​r​e​s​e​t + */ + password_reset: string + /** + * C​l​i​e​n​t​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​t​o​k​e​n​ ​a​d​d​e​d + */ + client_configuration_token_added: string } auditModule: { /** @@ -12507,6 +12527,10 @@ export type TranslationFunctions = { * VPN client failed MFA authentication */ vpn_client_mfa_failed: () => LocalizedString + /** + * Enrollment token added + */ + enrollment_token_added: () => LocalizedString /** * Enrollment started */ @@ -12639,6 +12663,22 @@ export type TranslationFunctions = { * Authentication key renamed */ authentication_key_renamed: () => LocalizedString + /** + * Password changed + */ + password_changed: () => LocalizedString + /** + * Password changed by admin + */ + password_changed_by_admin: () => LocalizedString + /** + * Password reset + */ + password_reset: () => LocalizedString + /** + * Client configuration token added + */ + client_configuration_token_added: () => LocalizedString } auditModule: { /** diff --git a/web/src/pages/activity/types.ts b/web/src/pages/activity/types.ts index 3f9d5e29c..741f41242 100644 --- a/web/src/pages/activity/types.ts +++ b/web/src/pages/activity/types.ts @@ -38,6 +38,7 @@ export type AuditEventType = | 'vpn_client_connected_mfa' | 'vpn_client_disconnected_mfa' | 'vpn_client_mfa_failed' + | 'enrollment_token_added' | 'enrollment_started' | 'enrollment_device_added' | 'enrollment_completed' @@ -71,6 +72,10 @@ export type AuditEventType = | 'authentication_key_added' | 'authentication_key_removed' | 'authentication_key_renamed' + | 'password_changed' + | 'password_changed_by_admin' + | 'password_reset' + | 'client_configuration_token_added'; export const auditEventTypeValues: AuditEventType[] = [ 'user_login', @@ -103,6 +108,7 @@ export const auditEventTypeValues: AuditEventType[] = [ 'vpn_client_connected_mfa', 'vpn_client_disconnected_mfa', 'vpn_client_mfa_failed', + 'enrollment_token_added', 'enrollment_started', 'enrollment_device_added', 'enrollment_completed', @@ -136,4 +142,8 @@ export const auditEventTypeValues: AuditEventType[] = [ 'authentication_key_added', 'authentication_key_removed', 'authentication_key_renamed', + 'password_changed', + 'password_changed_by_admin', + 'password_reset', + 'client_configuration_token_added', ]; From 254c5da599d37c0427a1805f8d69121a5d08667b Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 17 Jun 2025 13:28:31 +0200 Subject: [PATCH 13/23] Translation --- web/src/i18n/en/index.ts | 2 +- web/src/i18n/i18n-types.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 81ae294b9..9de72cd3f 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -1363,7 +1363,7 @@ Licensing information: [https://docs.defguard.net/enterprise/license](https://do modulesVisibility: { header: 'Modules Visibility', helper: `

- If your not using some modules you can disable their visibility. + Hide unused modules.

Read more in documentation. diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index ee9e5c34c..fc1b5afde 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -3329,7 +3329,7 @@ type RootTranslation = { header: string /** * <​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​I​f​ ​y​o​u​r​ ​n​o​t​ ​u​s​i​n​g​ ​s​o​m​e​ ​m​o​d​u​l​e​s​ ​y​o​u​ ​c​a​n​ ​d​i​s​a​b​l​e​ ​t​h​e​i​r​ ​v​i​s​i​b​i​l​i​t​y​.​ + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​H​i​d​e​ ​u​n​u​s​e​d​ ​m​o​d​u​l​e​s​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​a​ ​h​r​e​f​=​"​{​d​o​c​u​m​e​n​t​a​t​i​o​n​L​i​n​k​}​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​R​e​a​d​ ​m​o​r​e​ ​i​n​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​.​ @@ -9670,7 +9670,7 @@ export type TranslationFunctions = { header: () => LocalizedString /** *

- If your not using some modules you can disable their visibility. + Hide unused modules.

Read more in documentation. From 981eb58491942f919765f822a0fa1f61aba7f559 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 11:09:56 +0200 Subject: [PATCH 14/23] Store whole objects in the events --- .../src/db/models/audit_log/metadata.rs | 135 +++++---- .../src/db/models/authentication_key.rs | 2 +- crates/defguard_core/src/db/models/user.rs | 1 + .../defguard_core/src/db/models/webauthn.rs | 2 +- .../src/enterprise/db/models/audit_stream.rs | 2 +- .../enterprise/db/models/openid_provider.rs | 4 +- .../src/enterprise/handlers/api_tokens.rs | 8 +- .../src/enterprise/handlers/audit_stream.rs | 26 +- .../enterprise/handlers/openid_providers.rs | 16 +- crates/defguard_core/src/events.rs | 134 ++++----- crates/defguard_core/src/handlers/auth.rs | 5 +- crates/defguard_core/src/handlers/group.rs | 7 +- .../src/handlers/network_devices.rs | 35 ++- .../src/handlers/openid_clients.rs | 17 +- .../src/handlers/ssh_authorized_keys.rs | 17 +- crates/defguard_core/src/handlers/user.rs | 20 +- crates/defguard_core/src/handlers/webhooks.rs | 7 +- .../defguard_core/src/handlers/wireguard.rs | 64 ++--- crates/defguard_event_logger/src/lib.rs | 270 ++++++------------ crates/defguard_event_logger/src/message.rs | 186 ++++++------ crates/defguard_event_router/src/events.rs | 1 - .../defguard_event_router/src/handlers/api.rs | 228 +++++---------- crates/defguard_event_router/src/lib.rs | 2 +- 23 files changed, 494 insertions(+), 695 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index d5a811edc..94e881532 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -1,6 +1,11 @@ -use crate::db::{ - models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, WebHook, - WireguardNetwork, +use crate::{ + db::{ + models::{authentication_key::AuthenticationKey, oauth2client::OAuth2Client}, + Device, Group, Id, MFAMethod, User, WebAuthn, WebHook, WireguardNetwork, + }, + enterprise::db::models::{ + api_tokens::ApiToken, audit_stream::AuditStream, openid_provider::OpenIdProvider, + }, }; #[derive(Serialize)] @@ -9,75 +14,74 @@ pub struct MfaLoginMetadata { } #[derive(Serialize)] -pub struct DeviceAddedMetadata { - pub device_names: Vec, -} - -#[derive(Serialize)] -pub struct DeviceRemovedMetadata { - pub device_names: Vec, +pub struct DeviceMetadata { + pub owner: User, + pub device: Device, } #[derive(Serialize)] pub struct DeviceModifiedMetadata { - pub device_names: Vec, -} - -#[derive(Serialize)] -pub struct NetworkDeviceAddedMetadata { - pub device_id: Id, - pub device_name: String, - pub location_id: Id, - pub location: String, + pub owner: User, + pub before: Device, + pub after: Device, } #[derive(Serialize)] -pub struct NetworkDeviceRemovedMetadata { - pub device_id: Id, - pub device_name: String, - pub location_id: Id, - pub location: String, +pub struct NetworkDeviceMetadata { + pub device: Device, + pub location: WireguardNetwork, } #[derive(Serialize)] pub struct NetworkDeviceModifiedMetadata { - pub device_id: Id, - pub device_name: String, - pub location_id: Id, - pub location: String, + pub location: WireguardNetwork, + pub before: Device, + pub after: Device, } #[derive(Serialize)] -pub struct UserAddedMetadata { - pub username: String, +pub struct UserMetadata { + pub user: User, } #[derive(Serialize)] pub struct UserModifiedMetadata { - pub username: String, + pub before: User, + pub after: User, } #[derive(Serialize)] -pub struct UserRemovedMetadata { - pub username: String, +pub struct MfaSecurityKeyMetadata { + pub key: WebAuthnMetadata, } +// Avoid storing secrets in metadata #[derive(Serialize)] -pub struct MfaSecurityKeyRemovedMetadata { - pub key_id: Id, - pub key_name: String, +pub struct WebAuthnMetadata { + pub id: Id, + pub user_id: Id, + pub name: String, } -#[derive(Serialize)] -pub struct MfaSecurityKeyAddedMetadata { - pub key_id: Id, - pub key_name: String, +impl From> for WebAuthnMetadata { + fn from(value: WebAuthn) -> Self { + Self { + id: value.id, + user_id: value.user_id, + name: value.name, + } + } } #[derive(Serialize)] pub struct AuditStreamMetadata { - pub id: Id, - pub name: String, + pub stream: AuditStream, +} + +#[derive(Serialize)] +pub struct AuditStreamModifiedMetadata { + pub before: AuditStream, + pub after: AuditStream, } #[derive(Serialize)] @@ -99,7 +103,7 @@ pub struct EnrollmentDeviceAddedMetadata { } #[derive(Serialize)] -pub struct EnrollmentTokenAddedMetadata { +pub struct EnrollmentTokenMetadata { pub user: User, } @@ -108,36 +112,46 @@ pub struct VpnLocationMetadata { pub location: WireguardNetwork, } +#[derive(Serialize)] +pub struct VpnLocationModifiedMetadata { + pub before: WireguardNetwork, + pub after: WireguardNetwork, +} + #[derive(Serialize)] pub struct ApiTokenMetadata { pub owner: User, - pub token_name: String, + pub token: ApiToken, } #[derive(Serialize)] pub struct ApiTokenRenamedMetadata { pub owner: User, + pub token: ApiToken, pub old_name: String, pub new_name: String, } #[derive(Serialize)] pub struct OpenIdAppMetadata { - pub app_id: Id, - pub app_name: String, + pub app: OAuth2Client, +} + +#[derive(Serialize)] +pub struct OpenIdAppModifiedMetadata { + pub before: OAuth2Client, + pub after: OAuth2Client, } #[derive(Serialize)] pub struct OpenIdAppStateChangedMetadata { - pub app_id: Id, - pub app_name: String, + pub app: OAuth2Client, pub enabled: bool, } #[derive(Serialize)] pub struct OpenIdProviderMetadata { - pub provider_id: Id, - pub provider_name: String, + pub provider: OpenIdProvider, } #[derive(Serialize)] @@ -151,6 +165,12 @@ pub struct GroupMetadata { pub group: Group, } +#[derive(Serialize)] +pub struct GroupModifiedMetadata { + pub before: Group, + pub after: Group, +} + #[derive(Serialize)] pub struct GroupAssignedMetadata { pub group: Group, @@ -162,6 +182,12 @@ pub struct WebHookMetadata { pub webhook: WebHook, } +#[derive(Serialize)] +pub struct WebHookModifiedMetadata { + pub before: WebHook, + pub after: WebHook, +} + #[derive(Serialize)] pub struct WebHookStateChangedMetadata { pub webhook: WebHook, @@ -170,15 +196,12 @@ pub struct WebHookStateChangedMetadata { #[derive(Serialize)] pub struct AuthenticationKeyMetadata { - pub key_id: Id, - pub key_name: Option, - pub key_type: AuthenticationKeyType, + pub key: AuthenticationKey, } #[derive(Serialize)] pub struct AuthenticationKeyRenamedMetadata { - pub key_id: Id, - pub key_type: AuthenticationKeyType, + pub key: AuthenticationKey, pub old_name: Option, pub new_name: Option, } @@ -194,6 +217,6 @@ pub struct PasswordResetMetadata { } #[derive(Serialize)] -pub struct ClientConfigurationTokenAddedMetadata { +pub struct ClientConfigurationTokenMetadata { pub user: User, } diff --git a/crates/defguard_core/src/db/models/authentication_key.rs b/crates/defguard_core/src/db/models/authentication_key.rs index a61b53a39..260ab50fe 100644 --- a/crates/defguard_core/src/db/models/authentication_key.rs +++ b/crates/defguard_core/src/db/models/authentication_key.rs @@ -13,7 +13,7 @@ pub enum AuthenticationKeyType { #[derive(Deserialize, Model, Serialize)] #[table(authentication_key)] -pub(crate) struct AuthenticationKey { +pub struct AuthenticationKey { pub(crate) id: I, pub(crate) yubikey_id: Option, pub(crate) name: Option, diff --git a/crates/defguard_core/src/db/models/user.rs b/crates/defguard_core/src/db/models/user.rs index e6db06197..a7f391a09 100644 --- a/crates/defguard_core/src/db/models/user.rs +++ b/crates/defguard_core/src/db/models/user.rs @@ -93,6 +93,7 @@ pub struct UserDiagnostic { pub struct User { pub id: I, pub username: String, + #[serde(skip_serializing)] pub(crate) password_hash: Option, pub last_name: String, pub first_name: String, diff --git a/crates/defguard_core/src/db/models/webauthn.rs b/crates/defguard_core/src/db/models/webauthn.rs index cd5a0879a..47aad4dc1 100644 --- a/crates/defguard_core/src/db/models/webauthn.rs +++ b/crates/defguard_core/src/db/models/webauthn.rs @@ -5,7 +5,7 @@ use webauthn_rs::prelude::Passkey; use super::error::ModelError; use crate::db::{Id, NoId}; -#[derive(Model)] +#[derive(Model, Clone)] pub struct WebAuthn { pub(crate) id: I, pub(crate) user_id: Id, diff --git a/crates/defguard_core/src/enterprise/db/models/audit_stream.rs b/crates/defguard_core/src/enterprise/db/models/audit_stream.rs index ef3730ba6..1fff1822b 100644 --- a/crates/defguard_core/src/enterprise/db/models/audit_stream.rs +++ b/crates/defguard_core/src/enterprise/db/models/audit_stream.rs @@ -19,7 +19,7 @@ pub enum AuditStreamType { LogstashHttp, } -#[derive(Debug, Serialize, Model, FromRow)] +#[derive(Clone, Debug, Serialize, Model, FromRow)] #[table(audit_stream)] pub struct AuditStream { pub id: I, diff --git a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs index 4039727bb..f3cb40c7e 100644 --- a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs +++ b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs @@ -85,7 +85,7 @@ impl From for DirectorySyncTarget { } } -#[derive(Deserialize, Model, Serialize)] +#[derive(Clone, Deserialize, Model, Serialize)] pub struct OpenIdProvider { pub id: I, pub name: String, @@ -199,7 +199,7 @@ impl OpenIdProvider { query_as!( OpenIdProvider, "SELECT id, name, base_url, client_id, client_secret, display_name, \ - google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, + google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", \ directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", \ directory_sync_target \"directory_sync_target: DirectorySyncTarget\", \ diff --git a/crates/defguard_core/src/enterprise/handlers/api_tokens.rs b/crates/defguard_core/src/enterprise/handlers/api_tokens.rs index ad9ac1e2a..7b2e1291b 100644 --- a/crates/defguard_core/src/enterprise/handlers/api_tokens.rs +++ b/crates/defguard_core/src/enterprise/handlers/api_tokens.rs @@ -69,10 +69,7 @@ pub async fn add_api_token( if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { appstate.emit_event(ApiEvent { context, - event: ApiEventType::ApiTokenAdded { - owner, - token_name: token.name.clone(), - }, + event: ApiEventType::ApiTokenAdded { owner, token }, })?; } Ok(ApiResponse { @@ -122,7 +119,7 @@ pub async fn delete_api_token( context, event: ApiEventType::ApiTokenRemoved { owner, - token_name: token.name.clone(), + token: token.clone(), }, })?; } @@ -170,6 +167,7 @@ pub async fn rename_api_token( context, event: ApiEventType::ApiTokenRenamed { owner, + token: token.clone(), old_name, new_name, }, diff --git a/crates/defguard_core/src/enterprise/handlers/audit_stream.rs b/crates/defguard_core/src/enterprise/handlers/audit_stream.rs index 22bc66324..9d685ad1c 100644 --- a/crates/defguard_core/src/enterprise/handlers/audit_stream.rs +++ b/crates/defguard_core/src/enterprise/handlers/audit_stream.rs @@ -60,12 +60,8 @@ pub async fn create_audit_stream( info!("User {session_username} created audit stream"); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuditStreamCreated { - stream_id: stream.id, - stream_name: stream.name, - }, + event: ApiEventType::AuditStreamCreated { stream }, })?; - debug!("AuditStreamCreated api event sent"); Ok(ApiResponse { json: json!({}), status: StatusCode::CREATED, @@ -84,17 +80,22 @@ pub async fn modify_audit_stream( let session_username = &session.user.username; debug!("User {session_username} modifies audit stream "); if let Some(mut stream) = AuditStream::find_by_id(&appstate.pool, id).await? { + // store stream before modifications + let before = stream.clone(); //validate config let _ = AuditStreamConfig::from_serde_value(&data.stream_type, &data.stream_config)?; stream.name = data.name; stream.config = data.stream_config; stream.save(&appstate.pool).await?; - info!("User {session_username} modified audit stream"); + info!( + "User {session_username} modified audit stream {}", + stream.name + ); appstate.emit_event(ApiEvent { context, event: ApiEventType::AuditStreamModified { - stream_id: stream.id, - stream_name: stream.name, + before, + after: stream, }, })?; debug!("AuditStreamModified api event sent"); @@ -116,15 +117,10 @@ pub async fn delete_audit_stream( let session_username = &session.user.username; debug!("User {session_username} deleting Audit stream ({id})"); if let Some(stream) = AuditStream::find_by_id(&appstate.pool, id).await? { - let stream_id = stream.id; - let stream_name = stream.name.clone(); - stream.delete(&appstate.pool).await?; + stream.clone().delete(&appstate.pool).await?; appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuditStreamRemoved { - stream_id, - stream_name, - }, + event: ApiEventType::AuditStreamRemoved { stream }, })?; } else { return Err(crate::error::WebError::ObjectNotFound(format!( diff --git a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs index 256fba10e..4b6c1fe53 100644 --- a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs +++ b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs @@ -161,8 +161,7 @@ pub async fn add_openid_provider( appstate.emit_event(ApiEvent { context, event: ApiEventType::OpenIdProviderModified { - provider_id: new_provider.id, - provider_name: new_provider.name, + provider: new_provider, }, })?; @@ -216,19 +215,14 @@ pub async fn delete_openid_provider( ); let provider = OpenIdProvider::find_by_name(&appstate.pool, &provider_data.name).await?; if let Some(provider) = provider { - let provider_id = provider.id; - let provider_name = provider.name.clone(); - provider.delete(&appstate.pool).await?; + provider.clone().delete(&appstate.pool).await?; info!( - "User {} deleted OpenID provider {provider_name}", - session.user.username + "User {} deleted OpenID provider {}", + session.user.username, provider.name ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdProviderRemoved { - provider_id, - provider_name, - }, + event: ApiEventType::OpenIdProviderRemoved { provider }, })?; Ok(ApiResponse { json: json!({}), diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 4a33f8aad..7ce65f00d 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -1,8 +1,16 @@ use std::net::IpAddr; -use crate::db::{ - models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, WebHook, - WireguardNetwork, +use crate::{ + db::{ + models::{ + authentication_key::{AuthenticationKey, AuthenticationKeyType}, + oauth2client::OAuth2Client, + }, + Device, Group, Id, MFAMethod, User, WebAuthn, WebHook, WireguardNetwork, + }, + enterprise::db::models::{ + api_tokens::ApiToken, audit_stream::AuditStream, openid_provider::OpenIdProvider, + }, }; use chrono::{NaiveDateTime, Utc}; @@ -66,9 +74,9 @@ impl GrpcRequestContext { } } -#[derive(Debug)] pub enum ApiEventType { UserLogin, + UserLogout, UserLoginFailed, UserMfaLogin { mfa_method: MFAMethod, @@ -77,73 +85,69 @@ pub enum ApiEventType { mfa_method: MFAMethod, }, RecoveryCodeUsed, - UserLogout, + PasswordChangedByAdmin { + user: User, + }, + PasswordChanged, + PasswordReset { + user: User, + }, MfaDisabled, MfaTotpDisabled, MfaTotpEnabled, MfaEmailDisabled, MfaEmailEnabled, MfaSecurityKeyAdded { - key_id: Id, - key_name: String, + key: WebAuthn, }, MfaSecurityKeyRemoved { - key_id: Id, - key_name: String, + key: WebAuthn, }, UserAdded { - username: String, + user: User, }, UserRemoved { - username: String, + user: User, }, UserModified { - username: String, + before: User, + after: User, }, UserDeviceAdded { - device_id: Id, - owner: String, - device_name: String, + owner: User, + device: Device, }, UserDeviceRemoved { - device_id: Id, - owner: String, - device_name: String, + owner: User, + device: Device, }, UserDeviceModified { - device_id: Id, - owner: String, - device_name: String, + owner: User, + before: Device, + after: Device, }, NetworkDeviceAdded { - device_id: Id, - device_name: String, - location_id: Id, - location: String, + device: Device, + location: WireguardNetwork, }, NetworkDeviceRemoved { - device_id: Id, - device_name: String, - location_id: Id, - location: String, + device: Device, + location: WireguardNetwork, }, NetworkDeviceModified { - device_id: Id, - device_name: String, - location_id: Id, - location: String, + before: Device, + after: Device, + location: WireguardNetwork, }, AuditStreamCreated { - stream_id: Id, - stream_name: String, + stream: AuditStream, }, AuditStreamModified { - stream_id: Id, - stream_name: String, + before: AuditStream, + after: AuditStream, }, AuditStreamRemoved { - stream_id: Id, - stream_name: String, + stream: AuditStream, }, VpnLocationAdded { location: WireguardNetwork, @@ -152,45 +156,42 @@ pub enum ApiEventType { location: WireguardNetwork, }, VpnLocationModified { - location: WireguardNetwork, + before: WireguardNetwork, + after: WireguardNetwork, }, ApiTokenAdded { owner: User, - token_name: String, + token: ApiToken, }, ApiTokenRemoved { owner: User, - token_name: String, + token: ApiToken, }, ApiTokenRenamed { owner: User, + token: ApiToken, old_name: String, new_name: String, }, OpenIdAppAdded { - app_id: Id, - app_name: String, + app: OAuth2Client, }, OpenIdAppRemoved { - app_id: Id, - app_name: String, + app: OAuth2Client, }, OpenIdAppModified { - app_id: Id, - app_name: String, + before: OAuth2Client, + after: OAuth2Client, }, OpenIdAppStateChanged { - app_id: Id, - app_name: String, + app: OAuth2Client, enabled: bool, }, OpenIdProviderModified { - provider_id: Id, - provider_name: String, + provider: OpenIdProvider, }, OpenIdProviderRemoved { - provider_id: Id, - provider_name: String, + provider: OpenIdProvider, }, SettingsUpdated, SettingsUpdatedPartial, @@ -203,7 +204,8 @@ pub enum ApiEventType { group: Group, }, GroupModified { - group: Group, + before: Group, + after: Group, }, GroupRemoved { group: Group, @@ -220,7 +222,8 @@ pub enum ApiEventType { webhook: WebHook, }, WebHookModified { - webhook: WebHook, + before: WebHook, + after: WebHook, }, WebHookRemoved { webhook: WebHook, @@ -230,28 +233,16 @@ pub enum ApiEventType { enabled: bool, }, AuthenticationKeyAdded { - key_id: Id, - key_name: Option, - key_type: AuthenticationKeyType, + key: AuthenticationKey, }, AuthenticationKeyRemoved { - key_id: Id, - key_name: Option, - key_type: AuthenticationKeyType, + key: AuthenticationKey, }, AuthenticationKeyRenamed { - key_id: Id, - key_type: AuthenticationKeyType, + key: AuthenticationKey, old_name: Option, new_name: Option, }, - PasswordChangedByAdmin { - user: User, - }, - PasswordChanged, - PasswordReset { - user: User, - }, EnrollmentTokenAdded { user: User, }, @@ -261,7 +252,6 @@ pub enum ApiEventType { } /// Events from Web API -#[derive(Debug)] pub struct ApiEvent { pub context: ApiRequestContext, pub event: ApiEventType, diff --git a/crates/defguard_core/src/handlers/auth.rs b/crates/defguard_core/src/handlers/auth.rs index e681e0110..903811d64 100644 --- a/crates/defguard_core/src/handlers/auth.rs +++ b/crates/defguard_core/src/handlers/auth.rs @@ -503,10 +503,7 @@ pub async fn webauthn_finish( info!("Finished Webauthn registration for user {}", user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaSecurityKeyAdded { - key_id: webauthn.id, - key_name: webauthn.name, - }, + event: ApiEventType::MfaSecurityKeyAdded { key: webauthn }, })?; Ok(ApiResponse { diff --git a/crates/defguard_core/src/handlers/group.rs b/crates/defguard_core/src/handlers/group.rs index a4ab009f9..471946e78 100644 --- a/crates/defguard_core/src/handlers/group.rs +++ b/crates/defguard_core/src/handlers/group.rs @@ -403,6 +403,8 @@ pub(crate) async fn modify_group( error!(msg); return Err(WebError::ObjectNotFound(msg)); }; + // store group before modifications + let before = group.clone(); let mut add_to_ldap_groups: HashMap<&User, HashSet<&str>> = HashMap::new(); let mut remove_from_ldap_groups: HashMap<&User, HashSet<&str>> = HashMap::new(); @@ -491,7 +493,10 @@ pub(crate) async fn modify_group( info!("Modified group {}", group.name); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupModified { group }, + event: ApiEventType::GroupModified { + before, + after: group, + }, })?; Ok(ApiResponse::default()) } diff --git a/crates/defguard_core/src/handlers/network_devices.rs b/crates/defguard_core/src/handlers/network_devices.rs index 3405e9749..569094742 100644 --- a/crates/defguard_core/src/handlers/network_devices.rs +++ b/crates/defguard_core/src/handlers/network_devices.rs @@ -631,6 +631,13 @@ pub(crate) async fn add_network_device( session.session.device_info.clone().as_deref(), )?; + let result = AddNetworkDeviceResult { + config, + device: NetworkDeviceInfo::from_device(device.clone(), &mut transaction).await?, + }; + + transaction.commit().await?; + info!( "User {} added a new network device {device_name}.", user.username @@ -638,20 +645,11 @@ pub(crate) async fn add_network_device( appstate.emit_event(ApiEvent { context, event: ApiEventType::NetworkDeviceAdded { - device_id: device.id, - device_name: device.name.clone(), - location_id: network.id, - location: network.name, + device, + location: network, }, })?; - let result = AddNetworkDeviceResult { - config, - device: NetworkDeviceInfo::from_device(device, &mut transaction).await?, - }; - - transaction.commit().await?; - Ok(ApiResponse { json: json!(result), status: StatusCode::CREATED, @@ -681,6 +679,8 @@ pub async fn modify_network_device( error!("Failed to update device {device_id}, device not found"); WebError::ObjectNotFound(format!("Device {device_id} not found")) })?; + // store device before modifications + let before = device.clone(); let device_network = device .find_network_device_networks(&mut *transaction) .await? @@ -733,19 +733,18 @@ pub async fn modify_network_device( device_network.name ); } + let network_device_info = + NetworkDeviceInfo::from_device(device.clone(), &mut transaction).await?; + transaction.commit().await?; - let network_device_info = NetworkDeviceInfo::from_device(device, &mut transaction).await?; appstate.emit_event(ApiEvent { context, event: ApiEventType::NetworkDeviceModified { - device_id: network_device_info.id, - device_name: network_device_info.name.clone(), - location_id: device_network.id, - location: device_network.name, + before, + after: device, + location: device_network, }, })?; - transaction.commit().await?; - Ok(ApiResponse { json: json!(network_device_info), status: StatusCode::OK, diff --git a/crates/defguard_core/src/handlers/openid_clients.rs b/crates/defguard_core/src/handlers/openid_clients.rs index 41f6e2e59..e17556979 100644 --- a/crates/defguard_core/src/handlers/openid_clients.rs +++ b/crates/defguard_core/src/handlers/openid_clients.rs @@ -34,8 +34,7 @@ pub async fn add_openid_client( appstate.emit_event(ApiEvent { context, event: ApiEventType::OpenIdAppAdded { - app_id: client.id, - app_name: client.name.clone(), + app: client.clone(), }, })?; Ok(ApiResponse { @@ -92,6 +91,8 @@ pub async fn change_openid_client( ); let status = match OAuth2Client::find_by_client_id(&appstate.pool, &client_id).await? { Some(mut client) => { + // store client before mods + let before = client.clone(); client.name = data.name; client.redirect_uri = data.redirect_uri; client.enabled = data.enabled; @@ -104,8 +105,8 @@ pub async fn change_openid_client( appstate.emit_event(ApiEvent { context, event: ApiEventType::OpenIdAppModified { - app_id: client.id, - app_name: client.name, + before, + after: client, }, })?; StatusCode::OK @@ -141,9 +142,8 @@ pub async fn change_openid_client_state( appstate.emit_event(ApiEvent { context, event: ApiEventType::OpenIdAppStateChanged { - app_id: client.id, - app_name: client.name, enabled: client.enabled, + app: client, }, })?; StatusCode::OK @@ -176,10 +176,7 @@ pub async fn delete_openid_client( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdAppRemoved { - app_id: client.id, - app_name: client.name, - }, + event: ApiEventType::OpenIdAppRemoved { app: client }, })?; StatusCode::OK } diff --git a/crates/defguard_core/src/handlers/ssh_authorized_keys.rs b/crates/defguard_core/src/handlers/ssh_authorized_keys.rs index 8975d2434..cfbe17717 100644 --- a/crates/defguard_core/src/handlers/ssh_authorized_keys.rs +++ b/crates/defguard_core/src/handlers/ssh_authorized_keys.rs @@ -213,11 +213,7 @@ pub async fn add_authentication_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuthenticationKeyAdded { - key_id: key.id, - key_name: key.name, - key_type: key.key_type, - }, + event: ApiEventType::AuthenticationKeyAdded { key }, })?; Ok(ApiResponse { @@ -259,11 +255,7 @@ pub async fn delete_authentication_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuthenticationKeyRemoved { - key_id: key.id, - key_name: key.name, - key_type: key.key_type, - }, + event: ApiEventType::AuthenticationKeyRemoved { key }, })?; } else { error!("Key with id {} not found", key_id); @@ -314,10 +306,9 @@ pub async fn rename_authentication_key( appstate.emit_event(ApiEvent { context, event: ApiEventType::AuthenticationKeyRenamed { - key_id: key.id, old_name, - new_name: key.name, - key_type: key.key_type, + new_name: key.name.clone(), + key, }, })?; } else { diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index 8f34a926c..a14164153 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -342,9 +342,7 @@ pub async fn add_user( } appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserAdded { - username: user.username, - }, + event: ApiEventType::UserAdded { user }, })?; Ok(ApiResponse { json: json!(&user_info), @@ -640,6 +638,8 @@ pub async fn modify_user( ) -> ApiResult { debug!("User {} updating user {username}", session.user.username); let mut user = user_for_admin_or_self(&appstate.pool, &session, &username).await?; + // store user before mods + let before = user.clone(); let old_username = user.username.clone(); if let Err(err) = check_username(&user_info.username) { debug!("Username {} rejected: {err}", user_info.username); @@ -748,7 +748,8 @@ pub async fn modify_user( appstate.emit_event(ApiEvent { context, event: ApiEventType::UserModified { - username: user.username, + before, + after: user, }, })?; Ok(ApiResponse::default()) @@ -806,7 +807,8 @@ pub async fn delete_user( } else { None }; - user.delete_and_cleanup(&mut transaction, &appstate.wireguard_tx) + user.clone() + .delete_and_cleanup(&mut transaction, &appstate.wireguard_tx) .await?; appstate.trigger_action(AppEvent::UserDeleted(username.clone())); @@ -819,7 +821,7 @@ pub async fn delete_user( info!("User {} deleted user {}", session.user.username, &username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserRemoved { username }, + event: ApiEventType::UserRemoved { user }, })?; Ok(ApiResponse::default()) } else { @@ -1131,9 +1133,7 @@ pub async fn delete_security_key( let mut user = user_for_admin_or_self(&appstate.pool, &session, &username).await?; if let Some(webauthn) = WebAuthn::find_by_id(&appstate.pool, id).await? { if webauthn.user_id == user.id { - let key_id = webauthn.id; - let key_name = webauthn.name.clone(); - webauthn.delete(&appstate.pool).await?; + webauthn.clone().delete(&appstate.pool).await?; user.verify_mfa_state(&appstate.pool).await?; info!( "User {} deleted security key {id} for user {username}", @@ -1141,7 +1141,7 @@ pub async fn delete_security_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaSecurityKeyRemoved { key_id, key_name }, + event: ApiEventType::MfaSecurityKeyRemoved { key: webauthn }, })?; Ok(ApiResponse::default()) } else { diff --git a/crates/defguard_core/src/handlers/webhooks.rs b/crates/defguard_core/src/handlers/webhooks.rs index c3aeb353f..34dc4b312 100644 --- a/crates/defguard_core/src/handlers/webhooks.rs +++ b/crates/defguard_core/src/handlers/webhooks.rs @@ -78,6 +78,8 @@ pub async fn change_webhook( debug!("User {} updating webhook {id}", session.user.username); let status = match WebHook::find_by_id(&appstate.pool, id).await? { Some(mut webhook) => { + // store webhook before modifications + let before = webhook.clone(); webhook.url = data.url; webhook.description = data.description; webhook.token = data.token; @@ -90,7 +92,10 @@ pub async fn change_webhook( info!("User {} updated webhook {id}", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::WebHookModified { webhook }, + event: ApiEventType::WebHookModified { + before, + after: webhook, + }, })?; StatusCode::OK } diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index 04076fee0..419a88916 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -221,6 +221,8 @@ pub(crate) async fn modify_network( session.user.username ); let mut network = find_network(network_id, &appstate.pool).await?; + // store network before mods + let before = network.clone(); network.allowed_ips = data.parse_allowed_ips(); network.name = data.name; @@ -262,7 +264,8 @@ pub(crate) async fn modify_network( appstate.emit_event(ApiEvent { context, event: ApiEventType::VpnLocationModified { - location: network.clone(), + before, + after: network.clone(), }, })?; Ok(ApiResponse { @@ -788,20 +791,19 @@ pub(crate) async fn add_device( "User {} added device {device_name} for user {username}", session.user.username ); - // clone name to be used later - let device_name = device.name.clone(); - let device_id = device.id; - let result = AddDeviceResult { configs, device }; + let result = AddDeviceResult { + configs, + device: device.clone(), + }; update_counts(&appstate.pool).await?; appstate.emit_event(ApiEvent { context, event: ApiEventType::UserDeviceAdded { - device_id, - owner: username, - device_name, + device, + owner: user, }, })?; @@ -857,6 +859,8 @@ pub(crate) async fn modify_device( ) -> ApiResult { debug!("User {} updating device {device_id}", session.user.username); let mut device = device_for_admin_or_self(&appstate.pool, &session, device_id).await?; + // store device before mods + let before = device.clone(); let networks = WireguardNetwork::all(&appstate.pool).await?; if networks.is_empty() { @@ -882,7 +886,6 @@ pub(crate) async fn modify_device( device.update_from(data); // clone to use later - let device_name = device.name.clone(); device.save(&appstate.pool).await?; @@ -908,13 +911,13 @@ pub(crate) async fn modify_device( info!("User {} updated device {device_id}", session.user.username); - let owner = device.get_owner(&appstate.pool).await?.username; + let owner = device.get_owner(&appstate.pool).await?; appstate.emit_event(ApiEvent { context, event: ApiEventType::UserDeviceModified { owner, - device_id: device.id, - device_name, + before, + after: device.clone(), }, })?; @@ -1010,12 +1013,8 @@ pub(crate) async fn delete_device( // prepare device info let device_info = DeviceInfo::from_device(&mut *transaction, device.clone()).await?; - // clone to use later - let device_name = device.name.clone(); - let device_type = device.device_type.clone(); - // delete device before firewall config is generated - device.delete(&mut *transaction).await?; + device.clone().delete(&mut *transaction).await?; update_counts(&mut *transaction).await?; @@ -1043,20 +1042,12 @@ pub(crate) async fn delete_device( appstate.send_multiple_wireguard_events(events); // Emit event specific to the device type. - match device_type { + match device.device_type { DeviceType::User => { - let owner = device_info - .device - .get_owner(&mut *transaction) - .await? - .username; + let owner = device_info.device.get_owner(&mut *transaction).await?; appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserDeviceRemoved { - device_name, - owner, - device_id, - }, + event: ApiEventType::UserDeviceRemoved { device, owner }, })? } DeviceType::Network => { @@ -1067,18 +1058,19 @@ pub(crate) async fn delete_device( if let Some(location) = location { appstate.emit_event(ApiEvent { context, - event: ApiEventType::NetworkDeviceRemoved { - device_id, - device_name, - location_id: location.id, - location: location.name, - }, + event: ApiEventType::NetworkDeviceRemoved { device, location }, })?; } else { - error!("Network device {device_name}({device_id}) is assigned to non-existent location {}", network_info.network_id); + error!( + "Network device {}({}) is assigned to non-existent location {}", + device.name, device.id, network_info.network_id + ); } } else { - error!("Network device {device_name}({device_id}) has no network assigned"); + error!( + "Network device {}({}) has no network assigned", + device.name, device.id + ); } } }; diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 928274689..ad8f95980 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -11,15 +11,16 @@ use defguard_core::db::{ models::audit_log::{ metadata::{ ApiTokenMetadata, ApiTokenRenamedMetadata, AuditStreamMetadata, - AuthenticationKeyMetadata, AuthenticationKeyRenamedMetadata, - ClientConfigurationTokenAddedMetadata, DeviceAddedMetadata, DeviceModifiedMetadata, - DeviceRemovedMetadata, EnrollmentDeviceAddedMetadata, EnrollmentTokenAddedMetadata, - GroupAssignedMetadata, GroupMetadata, GroupsBulkAssignedMetadata, MfaLoginMetadata, - MfaSecurityKeyAddedMetadata, MfaSecurityKeyRemovedMetadata, NetworkDeviceAddedMetadata, - NetworkDeviceModifiedMetadata, NetworkDeviceRemovedMetadata, OpenIdAppMetadata, - OpenIdAppStateChangedMetadata, OpenIdProviderMetadata, PasswordChangedByAdminMetadata, - PasswordResetMetadata, UserAddedMetadata, UserModifiedMetadata, UserRemovedMetadata, - VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, WebHookMetadata, + AuditStreamModifiedMetadata, AuthenticationKeyMetadata, + AuthenticationKeyRenamedMetadata, ClientConfigurationTokenMetadata, DeviceMetadata, + DeviceModifiedMetadata, EnrollmentDeviceAddedMetadata, EnrollmentTokenMetadata, + GroupAssignedMetadata, GroupMetadata, GroupModifiedMetadata, + GroupsBulkAssignedMetadata, MfaLoginMetadata, MfaSecurityKeyMetadata, + NetworkDeviceMetadata, NetworkDeviceModifiedMetadata, OpenIdAppMetadata, + OpenIdAppModifiedMetadata, OpenIdAppStateChangedMetadata, OpenIdProviderMetadata, + PasswordChangedByAdminMetadata, PasswordResetMetadata, UserMetadata, + UserModifiedMetadata, VpnClientMetadata, VpnClientMfaMetadata, VpnLocationMetadata, + VpnLocationModifiedMetadata, WebHookMetadata, WebHookModifiedMetadata, WebHookStateChangedMetadata, }, AuditEvent, AuditModule, EventType, @@ -85,36 +86,24 @@ pub async fn run_event_logger( serde_json::to_value(MfaLoginMetadata { mfa_method }).ok(), ), DefguardEvent::UserLogout => (EventType::UserLogout, None), - DefguardEvent::UserDeviceAdded { - device_id: _, - device_name, - owner: _, - } => ( + DefguardEvent::UserDeviceAdded { owner, device } => ( EventType::DeviceAdded, - serde_json::to_value(DeviceAddedMetadata { - device_names: vec![device_name], - }) - .ok(), + serde_json::to_value(DeviceMetadata { owner, device }).ok(), ), - DefguardEvent::UserDeviceRemoved { - device_id: _, - device_name, - owner: _, - } => ( + DefguardEvent::UserDeviceRemoved { owner, device } => ( EventType::DeviceRemoved, - serde_json::to_value(DeviceRemovedMetadata { - device_names: vec![device_name], - }) - .ok(), + serde_json::to_value(DeviceMetadata { owner, device }).ok(), ), DefguardEvent::UserDeviceModified { - device_id: _, - device_name, - owner: _, + owner, + before, + after, } => ( EventType::DeviceModified, serde_json::to_value(DeviceModifiedMetadata { - device_names: vec![device_name], + owner, + before, + after, }) .ok(), ), @@ -129,138 +118,91 @@ pub async fn run_event_logger( DefguardEvent::MfaTotpDisabled => (EventType::MfaTotpDisabled, None), DefguardEvent::MfaEmailEnabled => (EventType::MfaEmailEnabled, None), DefguardEvent::MfaEmailDisabled => (EventType::MfaEmailDisabled, None), - DefguardEvent::MfaSecurityKeyAdded { key_id, key_name } => ( + DefguardEvent::MfaSecurityKeyAdded { key } => ( EventType::MfaSecurityKeyAdded, - serde_json::to_value(MfaSecurityKeyAddedMetadata { - key_id, - key_name, - }) - .ok(), + serde_json::to_value(MfaSecurityKeyMetadata { key: key.into() }) + .ok(), ), - DefguardEvent::MfaSecurityKeyRemoved { key_id, key_name } => ( + DefguardEvent::MfaSecurityKeyRemoved { key } => ( EventType::MfaSecurityKeyRemoved, - serde_json::to_value(MfaSecurityKeyRemovedMetadata { - key_id, - key_name, - }) - .ok(), + serde_json::to_value(MfaSecurityKeyMetadata { key: key.into() }) + .ok(), ), - DefguardEvent::AuthenticationKeyAdded { - key_id, - key_name, - key_type, - } => ( + DefguardEvent::AuthenticationKeyAdded { key } => ( EventType::AuthenticationKeyAdded, - serde_json::to_value(AuthenticationKeyMetadata { - key_id, - key_name, - key_type, - }) - .ok(), + serde_json::to_value(AuthenticationKeyMetadata { key }).ok(), ), - DefguardEvent::AuthenticationKeyRemoved { - key_id, - key_name, - key_type, - } => ( + DefguardEvent::AuthenticationKeyRemoved { key } => ( EventType::AuthenticationKeyRemoved, - serde_json::to_value(AuthenticationKeyMetadata { - key_id, - key_name, - key_type, - }) - .ok(), + serde_json::to_value(AuthenticationKeyMetadata { key }).ok(), ), DefguardEvent::AuthenticationKeyRenamed { - key_id, + key, old_name, new_name, - key_type, } => ( EventType::AuthenticationKeyRenamed, serde_json::to_value(AuthenticationKeyRenamedMetadata { - key_id, + key, old_name, new_name, - key_type, }) .ok(), ), - DefguardEvent::ApiTokenAdded { owner, token_name } => ( + DefguardEvent::ApiTokenAdded { owner, token } => ( EventType::ApiTokenAdded, - serde_json::to_value(ApiTokenMetadata { owner, token_name }).ok(), + serde_json::to_value(ApiTokenMetadata { owner, token }).ok(), ), - DefguardEvent::ApiTokenRemoved { owner, token_name } => ( + DefguardEvent::ApiTokenRemoved { owner, token } => ( EventType::ApiTokenRemoved, - serde_json::to_value(ApiTokenMetadata { owner, token_name }).ok(), + serde_json::to_value(ApiTokenMetadata { owner, token }).ok(), ), DefguardEvent::ApiTokenRenamed { owner, + token, old_name, new_name, } => ( EventType::ApiTokenRenamed, serde_json::to_value(ApiTokenRenamedMetadata { owner, + token, old_name, new_name, }) .ok(), ), - DefguardEvent::UserAdded { username } => ( + DefguardEvent::UserAdded { user } => ( EventType::UserAdded, - serde_json::to_value(UserAddedMetadata { username }).ok(), + serde_json::to_value(UserMetadata { user }).ok(), ), - DefguardEvent::UserRemoved { username } => ( + DefguardEvent::UserRemoved { user } => ( EventType::UserRemoved, - serde_json::to_value(UserRemovedMetadata { username }).ok(), + serde_json::to_value(UserMetadata { user }).ok(), ), - DefguardEvent::UserModified { username } => ( + DefguardEvent::UserModified { before, after } => ( EventType::UserModified, - serde_json::to_value(UserModifiedMetadata { username }).ok(), + serde_json::to_value(UserModifiedMetadata { before, after }).ok(), ), - DefguardEvent::UserDisabled { username: _ } => todo!(), - DefguardEvent::NetworkDeviceAdded { - device_id, - device_name, - location_id, - location, - } => ( + DefguardEvent::NetworkDeviceAdded { device, location } => ( EventType::NetworkDeviceAdded, - serde_json::to_value(NetworkDeviceAddedMetadata { - device_id, - device_name, - location_id, - location, - }) - .ok(), + serde_json::to_value(NetworkDeviceMetadata { device, location }) + .ok(), ), - DefguardEvent::NetworkDeviceRemoved { - device_id, - device_name, - location_id, - location, - } => ( + DefguardEvent::NetworkDeviceRemoved { device, location } => ( EventType::NetworkDeviceRemoved, - serde_json::to_value(NetworkDeviceRemovedMetadata { - device_id, - device_name, - location_id, - location, - }) - .ok(), + serde_json::to_value(NetworkDeviceMetadata { device, location }) + .ok(), ), DefguardEvent::NetworkDeviceModified { - device_id, - device_name, - location_id, location, + before, + after, } => ( EventType::NetworkDeviceModified, serde_json::to_value(NetworkDeviceModifiedMetadata { - device_id, - device_name, - location_id, + before, + after, location, }) .ok(), @@ -273,56 +215,39 @@ pub async fn run_event_logger( EventType::VpnLocationRemoved, serde_json::to_value(VpnLocationMetadata { location }).ok(), ), - DefguardEvent::VpnLocationModified { location } => ( + DefguardEvent::VpnLocationModified { before, after } => ( EventType::VpnLocationModified, - serde_json::to_value(VpnLocationMetadata { location }).ok(), + serde_json::to_value(VpnLocationModifiedMetadata { before, after }) + .ok(), ), - DefguardEvent::OpenIdAppAdded { app_id, app_name } => ( + DefguardEvent::OpenIdAppAdded { app } => ( EventType::OpenIdAppAdded, - serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), + serde_json::to_value(OpenIdAppMetadata { app }).ok(), ), - DefguardEvent::OpenIdAppRemoved { app_id, app_name } => ( + DefguardEvent::OpenIdAppRemoved { app } => ( EventType::OpenIdAppRemoved, - serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), + serde_json::to_value(OpenIdAppMetadata { app }).ok(), ), - DefguardEvent::OpenIdAppModified { app_id, app_name } => ( + DefguardEvent::OpenIdAppModified { before, after } => ( EventType::OpenIdAppModified, - serde_json::to_value(OpenIdAppMetadata { app_id, app_name }).ok(), + serde_json::to_value(OpenIdAppModifiedMetadata { before, after }) + .ok(), ), - DefguardEvent::OpenIdAppStateChanged { - app_id, - app_name, - enabled, - } => ( + DefguardEvent::OpenIdAppStateChanged { app, enabled } => ( EventType::OpenIdAppStateChanged, serde_json::to_value(OpenIdAppStateChangedMetadata { - app_id, - app_name, + app, enabled, }) .ok(), ), - DefguardEvent::OpenIdProviderModified { - provider_id, - provider_name, - } => ( + DefguardEvent::OpenIdProviderModified { provider } => ( EventType::OpenIdProviderModified, - serde_json::to_value(OpenIdProviderMetadata { - provider_id, - provider_name, - }) - .ok(), + serde_json::to_value(OpenIdProviderMetadata { provider }).ok(), ), - DefguardEvent::OpenIdProviderRemoved { - provider_id, - provider_name, - } => ( + DefguardEvent::OpenIdProviderRemoved { provider } => ( EventType::OpenIdProviderRemoved, - serde_json::to_value(OpenIdProviderMetadata { - provider_id, - provider_name, - }) - .ok(), + serde_json::to_value(OpenIdProviderMetadata { provider }).ok(), ), DefguardEvent::SettingsUpdated => (EventType::SettingsUpdated, None), DefguardEvent::SettingsUpdatedPartial => { @@ -331,38 +256,18 @@ pub async fn run_event_logger( DefguardEvent::SettingsDefaultBrandingRestored => { (EventType::SettingsDefaultBrandingRestored, None) } - DefguardEvent::AuditStreamCreated { - stream_id, - stream_name, - } => ( + DefguardEvent::AuditStreamCreated { stream } => ( EventType::AuditStreamCreated, - serde_json::to_value(AuditStreamMetadata { - id: stream_id, - name: stream_name, - }) - .ok(), + serde_json::to_value(AuditStreamMetadata { stream }).ok(), ), - DefguardEvent::AuditStreamRemoved { - stream_id, - stream_name, - } => ( + DefguardEvent::AuditStreamRemoved { stream } => ( EventType::AuditStreamRemoved, - serde_json::to_value(AuditStreamMetadata { - id: stream_id, - name: stream_name, - }) - .ok(), + serde_json::to_value(AuditStreamMetadata { stream }).ok(), ), - DefguardEvent::AuditStreamModified { - stream_id, - stream_name, - } => ( + DefguardEvent::AuditStreamModified { before, after } => ( EventType::AuditStreamModified, - serde_json::to_value(AuditStreamMetadata { - id: stream_id, - name: stream_name, - }) - .ok(), + serde_json::to_value(AuditStreamModifiedMetadata { before, after }) + .ok(), ), DefguardEvent::GroupsBulkAssigned { users, groups } => ( EventType::GroupsBulkAssigned, @@ -373,9 +278,9 @@ pub async fn run_event_logger( EventType::GroupAdded, serde_json::to_value(GroupMetadata { group }).ok(), ), - DefguardEvent::GroupModified { group } => ( + DefguardEvent::GroupModified { before, after } => ( EventType::GroupModified, - serde_json::to_value(GroupMetadata { group }).ok(), + serde_json::to_value(GroupModifiedMetadata { before, after }).ok(), ), DefguardEvent::GroupRemoved { group } => ( EventType::GroupRemoved, @@ -393,9 +298,10 @@ pub async fn run_event_logger( EventType::WebHookAdded, serde_json::to_value(WebHookMetadata { webhook }).ok(), ), - DefguardEvent::WebHookModified { webhook } => ( + DefguardEvent::WebHookModified { before, after } => ( EventType::WebHookModified, - serde_json::to_value(WebHookMetadata { webhook }).ok(), + serde_json::to_value(WebHookModifiedMetadata { before, after }) + .ok(), ), DefguardEvent::WebHookRemoved { webhook } => ( EventType::WebHookRemoved, @@ -415,10 +321,12 @@ pub async fn run_event_logger( ), DefguardEvent::ClientConfigurationTokenAdded { user } => ( EventType::ClientConfigurationTokenAdded, - serde_json::to_value(ClientConfigurationTokenAddedMetadata { - user, - }) - .ok(), + serde_json::to_value(ClientConfigurationTokenMetadata { user }) + .ok(), + ), + DefguardEvent::EnrollmentTokenAdded { user } => ( + EventType::EnrollmentTokenAdded, + serde_json::to_value(EnrollmentTokenMetadata { user }).ok(), ), }; (module, event_type, metadata) @@ -495,7 +403,7 @@ pub async fn run_event_logger( } EnrollmentEvent::TokenAdded { user } => ( EventType::EnrollmentTokenAdded, - serde_json::to_value(EnrollmentTokenAddedMetadata { user }).ok(), + serde_json::to_value(EnrollmentTokenMetadata { user }).ok(), ), }; (module, event_type, metadata) diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index 1f8769e59..e67747531 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -3,8 +3,14 @@ use std::net::IpAddr; use defguard_core::{ db::{ - models::authentication_key::AuthenticationKeyType, Device, Group, Id, MFAMethod, User, - WebHook, WireguardNetwork, + models::{ + authentication_key::{AuthenticationKey, AuthenticationKeyType}, + oauth2client::OAuth2Client, + }, + Device, Group, Id, MFAMethod, User, WebAuthn, WebHook, WireguardNetwork, + }, + enterprise::db::models::{ + api_tokens::ApiToken, audit_stream::AuditStream, openid_provider::OpenIdProvider, }, events::{ApiRequestContext, BidiRequestContext, GrpcRequestContext, InternalEventContext}, }; @@ -90,8 +96,8 @@ impl From for EventContext { /// Represents audit events related to actions performed in Web UI pub enum DefguardEvent { - // authentication UserLogin, + UserLogout, UserLoginFailed, UserMfaLogin { mfa_method: MFAMethod, @@ -99,7 +105,6 @@ pub enum DefguardEvent { UserMfaLoginFailed { mfa_method: MFAMethod, }, - UserLogout, RecoveryCodeUsed, PasswordChangedByAdmin { user: User, @@ -108,99 +113,63 @@ pub enum DefguardEvent { PasswordReset { user: User, }, - // user MFA management MfaDisabled, - MfaTotpEnabled, MfaTotpDisabled, - MfaEmailEnabled, + MfaTotpEnabled, MfaEmailDisabled, + MfaEmailEnabled, MfaSecurityKeyAdded { - key_id: Id, - key_name: String, + key: WebAuthn, }, MfaSecurityKeyRemoved { - key_id: Id, - key_name: String, - }, - // authentication key management - AuthenticationKeyAdded { - key_id: Id, - key_name: Option, - key_type: AuthenticationKeyType, - }, - AuthenticationKeyRemoved { - key_id: Id, - key_name: Option, - key_type: AuthenticationKeyType, - }, - AuthenticationKeyRenamed { - key_id: Id, - key_type: AuthenticationKeyType, - old_name: Option, - new_name: Option, - }, - // API token management - ApiTokenAdded { - owner: User, - token_name: String, - }, - ApiTokenRemoved { - owner: User, - token_name: String, - }, - ApiTokenRenamed { - owner: User, - old_name: String, - new_name: String, + key: WebAuthn, }, - // user management UserAdded { - username: String, + user: User, }, UserRemoved { - username: String, + user: User, }, UserModified { - username: String, - }, - UserDisabled { - username: String, + before: User, + after: User, }, - // device management UserDeviceAdded { - device_id: Id, - device_name: String, - owner: String, + owner: User, + device: Device, }, UserDeviceRemoved { - device_id: Id, - device_name: String, - owner: String, + owner: User, + device: Device, }, UserDeviceModified { - device_id: Id, - device_name: String, - owner: String, + owner: User, + before: Device, + after: Device, }, NetworkDeviceAdded { - device_id: Id, - device_name: String, - location_id: Id, - location: String, + device: Device, + location: WireguardNetwork, }, NetworkDeviceRemoved { - device_id: Id, - device_name: String, - location_id: Id, - location: String, + device: Device, + location: WireguardNetwork, }, NetworkDeviceModified { - device_id: Id, - device_name: String, - location_id: Id, - location: String, + before: Device, + after: Device, + location: WireguardNetwork, + }, + AuditStreamCreated { + stream: AuditStream, + }, + AuditStreamModified { + before: AuditStream, + after: AuditStream, + }, + AuditStreamRemoved { + stream: AuditStream, }, - // VPN location management VpnLocationAdded { location: WireguardNetwork, }, @@ -208,53 +177,46 @@ pub enum DefguardEvent { location: WireguardNetwork, }, VpnLocationModified { - location: WireguardNetwork, + before: WireguardNetwork, + after: WireguardNetwork, + }, + ApiTokenAdded { + owner: User, + token: ApiToken, + }, + ApiTokenRemoved { + owner: User, + token: ApiToken, + }, + ApiTokenRenamed { + owner: User, + token: ApiToken, + old_name: String, + new_name: String, }, - // OpenID app management OpenIdAppAdded { - app_id: Id, - app_name: String, + app: OAuth2Client, }, OpenIdAppRemoved { - app_id: Id, - app_name: String, + app: OAuth2Client, }, OpenIdAppModified { - app_id: Id, - app_name: String, + before: OAuth2Client, + after: OAuth2Client, }, OpenIdAppStateChanged { - app_id: Id, - app_name: String, + app: OAuth2Client, enabled: bool, }, - // OpenID provider management OpenIdProviderModified { - provider_id: Id, - provider_name: String, + provider: OpenIdProvider, }, OpenIdProviderRemoved { - provider_id: Id, - provider_name: String, + provider: OpenIdProvider, }, - // settings management SettingsUpdated, SettingsUpdatedPartial, SettingsDefaultBrandingRestored, - // audit stream management - AuditStreamCreated { - stream_id: Id, - stream_name: String, - }, - AuditStreamModified { - stream_id: Id, - stream_name: String, - }, - AuditStreamRemoved { - stream_id: Id, - stream_name: String, - }, - // groups management GroupsBulkAssigned { users: Vec>, groups: Vec>, @@ -263,7 +225,8 @@ pub enum DefguardEvent { group: Group, }, GroupModified { - group: Group, + before: Group, + after: Group, }, GroupRemoved { group: Group, @@ -280,7 +243,8 @@ pub enum DefguardEvent { webhook: WebHook, }, WebHookModified { - webhook: WebHook, + before: WebHook, + after: WebHook, }, WebHookRemoved { webhook: WebHook, @@ -289,6 +253,20 @@ pub enum DefguardEvent { webhook: WebHook, enabled: bool, }, + AuthenticationKeyAdded { + key: AuthenticationKey, + }, + AuthenticationKeyRemoved { + key: AuthenticationKey, + }, + AuthenticationKeyRenamed { + key: AuthenticationKey, + old_name: Option, + new_name: Option, + }, + EnrollmentTokenAdded { + user: User, + }, ClientConfigurationTokenAdded { user: User, }, diff --git a/crates/defguard_event_router/src/events.rs b/crates/defguard_event_router/src/events.rs index 02c0fb10b..0ee2fb18f 100644 --- a/crates/defguard_event_router/src/events.rs +++ b/crates/defguard_event_router/src/events.rs @@ -6,7 +6,6 @@ use defguard_core::events::{ApiEvent, BidiStreamEvent, GrpcEvent, InternalEvent} /// The enum itself is organized based on event source to make splitting logic into smaller chunks easier. // TODO: remove lint override below once all events are updated to pass whole objects #[allow(clippy::large_enum_variant)] -#[derive(Debug)] pub enum Event { Api(ApiEvent), Grpc(GrpcEvent), diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 949ea3415..32c68d19b 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -6,7 +6,7 @@ use crate::{error::EventRouterError, EventRouter}; impl EventRouter { pub(crate) fn handle_api_event(&self, event: ApiEvent) -> Result<(), EventRouterError> { - debug!("Processing API event: {event:?}"); + debug!("Processing API event"); let logger_event = match event.event { ApiEventType::UserLogin => LoggerEvent::Defguard(DefguardEvent::UserLogin), ApiEventType::UserLoginFailed => LoggerEvent::Defguard(DefguardEvent::UserLoginFailed), @@ -20,14 +20,14 @@ impl EventRouter { LoggerEvent::Defguard(DefguardEvent::RecoveryCodeUsed) } ApiEventType::UserLogout => LoggerEvent::Defguard(DefguardEvent::UserLogout), - ApiEventType::UserAdded { username } => { - LoggerEvent::Defguard(DefguardEvent::UserAdded { username }) + ApiEventType::UserAdded { user } => { + LoggerEvent::Defguard(DefguardEvent::UserAdded { user }) } - ApiEventType::UserRemoved { username } => { - LoggerEvent::Defguard(DefguardEvent::UserRemoved { username }) + ApiEventType::UserRemoved { user } => { + LoggerEvent::Defguard(DefguardEvent::UserRemoved { user }) } - ApiEventType::UserModified { username } => { - LoggerEvent::Defguard(DefguardEvent::UserModified { username }) + ApiEventType::UserModified { before, after } => { + LoggerEvent::Defguard(DefguardEvent::UserModified { before, after }) } ApiEventType::MfaDisabled => LoggerEvent::Defguard(DefguardEvent::MfaDisabled), ApiEventType::MfaTotpDisabled => LoggerEvent::Defguard(DefguardEvent::MfaTotpDisabled), @@ -36,104 +36,56 @@ impl EventRouter { LoggerEvent::Defguard(DefguardEvent::MfaEmailDisabled) } ApiEventType::MfaEmailEnabled => LoggerEvent::Defguard(DefguardEvent::MfaEmailEnabled), - ApiEventType::MfaSecurityKeyAdded { key_id, key_name } => { - LoggerEvent::Defguard(DefguardEvent::MfaSecurityKeyAdded { key_id, key_name }) + ApiEventType::MfaSecurityKeyAdded { key } => { + LoggerEvent::Defguard(DefguardEvent::MfaSecurityKeyAdded { key }) } - ApiEventType::MfaSecurityKeyRemoved { key_id, key_name } => { - LoggerEvent::Defguard(DefguardEvent::MfaSecurityKeyRemoved { key_id, key_name }) + ApiEventType::MfaSecurityKeyRemoved { key } => { + LoggerEvent::Defguard(DefguardEvent::MfaSecurityKeyRemoved { key }) + } + ApiEventType::UserDeviceAdded { owner, device } => { + LoggerEvent::Defguard(DefguardEvent::UserDeviceAdded { device, owner }) + } + ApiEventType::UserDeviceRemoved { owner, device } => { + LoggerEvent::Defguard(DefguardEvent::UserDeviceRemoved { device, owner }) } - ApiEventType::UserDeviceAdded { - owner, - device_id, - device_name, - } => LoggerEvent::Defguard(DefguardEvent::UserDeviceAdded { - device_name, - device_id, - owner, - }), - ApiEventType::UserDeviceRemoved { - owner, - device_id, - device_name, - } => LoggerEvent::Defguard(DefguardEvent::UserDeviceRemoved { - device_name, - device_id, - owner, - }), ApiEventType::UserDeviceModified { owner, - device_id, - device_name, + before, + after, } => LoggerEvent::Defguard(DefguardEvent::UserDeviceModified { - device_name, - device_id, owner, + before, + after, }), - ApiEventType::NetworkDeviceAdded { - device_id, - device_name, - location_id, - location, - } => LoggerEvent::Defguard(DefguardEvent::NetworkDeviceAdded { - device_id, - device_name, - location_id, - location, - }), + ApiEventType::NetworkDeviceAdded { device, location } => { + LoggerEvent::Defguard(DefguardEvent::NetworkDeviceAdded { device, location }) + } ApiEventType::NetworkDeviceModified { - device_id, - device_name, - location_id, + before, + after, location, } => LoggerEvent::Defguard(DefguardEvent::NetworkDeviceModified { - device_id, - device_name, - location_id, + before, + after, location, }), - ApiEventType::NetworkDeviceRemoved { - device_id, - device_name, - location_id, - location, - } => LoggerEvent::Defguard(DefguardEvent::NetworkDeviceRemoved { - device_id, - device_name, - location_id, - location, - }), - ApiEventType::AuditStreamCreated { - stream_id, - stream_name, - } => { + ApiEventType::NetworkDeviceRemoved { device, location } => { + LoggerEvent::Defguard(DefguardEvent::NetworkDeviceRemoved { device, location }) + } + ApiEventType::AuditStreamCreated { stream } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(DefguardEvent::AuditStreamCreated { - stream_id, - stream_name, - }) - } - ApiEventType::AuditStreamModified { - stream_id, - stream_name, - } => { + LoggerEvent::Defguard(DefguardEvent::AuditStreamCreated { stream }) + } + ApiEventType::AuditStreamModified { before, after } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(DefguardEvent::AuditStreamModified { - stream_id, - stream_name, - }) - } - ApiEventType::AuditStreamRemoved { - stream_id, - stream_name, - } => { + LoggerEvent::Defguard(DefguardEvent::AuditStreamModified { before, after }) + } + ApiEventType::AuditStreamRemoved { stream } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(DefguardEvent::AuditStreamRemoved { - stream_id, - stream_name, - }) + LoggerEvent::Defguard(DefguardEvent::AuditStreamRemoved { stream }) } ApiEventType::VpnLocationAdded { location } => { LoggerEvent::Defguard(DefguardEvent::VpnLocationAdded { location }) @@ -141,56 +93,44 @@ impl EventRouter { ApiEventType::VpnLocationRemoved { location } => { LoggerEvent::Defguard(DefguardEvent::VpnLocationRemoved { location }) } - ApiEventType::VpnLocationModified { location } => { - LoggerEvent::Defguard(DefguardEvent::VpnLocationModified { location }) + ApiEventType::VpnLocationModified { before, after } => { + LoggerEvent::Defguard(DefguardEvent::VpnLocationModified { before, after }) } - ApiEventType::ApiTokenAdded { owner, token_name } => { - LoggerEvent::Defguard(DefguardEvent::ApiTokenAdded { owner, token_name }) + ApiEventType::ApiTokenAdded { owner, token } => { + LoggerEvent::Defguard(DefguardEvent::ApiTokenAdded { owner, token }) } - ApiEventType::ApiTokenRemoved { owner, token_name } => { - LoggerEvent::Defguard(DefguardEvent::ApiTokenRemoved { owner, token_name }) + ApiEventType::ApiTokenRemoved { owner, token } => { + LoggerEvent::Defguard(DefguardEvent::ApiTokenRemoved { owner, token }) } ApiEventType::ApiTokenRenamed { owner, + token, old_name, new_name, } => LoggerEvent::Defguard(DefguardEvent::ApiTokenRenamed { owner, + token, old_name, new_name, }), - ApiEventType::OpenIdAppAdded { app_id, app_name } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppAdded { app_id, app_name }) - } - ApiEventType::OpenIdAppRemoved { app_id, app_name } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppRemoved { app_id, app_name }) - } - ApiEventType::OpenIdAppModified { app_id, app_name } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppModified { app_id, app_name }) - } - ApiEventType::OpenIdAppStateChanged { - app_id, - app_name, - enabled, - } => LoggerEvent::Defguard(DefguardEvent::OpenIdAppStateChanged { - app_id, - app_name, - enabled, - }), - ApiEventType::OpenIdProviderRemoved { - provider_id, - provider_name, - } => LoggerEvent::Defguard(DefguardEvent::OpenIdProviderRemoved { - provider_id, - provider_name, - }), - ApiEventType::OpenIdProviderModified { - provider_id, - provider_name, - } => LoggerEvent::Defguard(DefguardEvent::OpenIdProviderModified { - provider_id, - provider_name, - }), + ApiEventType::OpenIdAppAdded { app } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppAdded { app }) + } + ApiEventType::OpenIdAppRemoved { app } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppRemoved { app }) + } + ApiEventType::OpenIdAppModified { before, after } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppModified { before, after }) + } + ApiEventType::OpenIdAppStateChanged { app, enabled } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdAppStateChanged { app, enabled }) + } + ApiEventType::OpenIdProviderRemoved { provider } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdProviderRemoved { provider }) + } + ApiEventType::OpenIdProviderModified { provider } => { + LoggerEvent::Defguard(DefguardEvent::OpenIdProviderModified { provider }) + } ApiEventType::SettingsUpdated => LoggerEvent::Defguard(DefguardEvent::SettingsUpdated), ApiEventType::SettingsUpdatedPartial => { LoggerEvent::Defguard(DefguardEvent::SettingsUpdatedPartial) @@ -204,8 +144,8 @@ impl EventRouter { ApiEventType::GroupAdded { group } => { LoggerEvent::Defguard(DefguardEvent::GroupAdded { group }) } - ApiEventType::GroupModified { group } => { - LoggerEvent::Defguard(DefguardEvent::GroupModified { group }) + ApiEventType::GroupModified { before, after } => { + LoggerEvent::Defguard(DefguardEvent::GroupModified { before, after }) } ApiEventType::GroupRemoved { group } => { LoggerEvent::Defguard(DefguardEvent::GroupRemoved { group }) @@ -219,8 +159,8 @@ impl EventRouter { ApiEventType::WebHookAdded { webhook } => { LoggerEvent::Defguard(DefguardEvent::WebHookAdded { webhook }) } - ApiEventType::WebHookModified { webhook } => { - LoggerEvent::Defguard(DefguardEvent::WebHookModified { webhook }) + ApiEventType::WebHookModified { before, after } => { + LoggerEvent::Defguard(DefguardEvent::WebHookModified { before, after }) } ApiEventType::WebHookRemoved { webhook } => { LoggerEvent::Defguard(DefguardEvent::WebHookRemoved { webhook }) @@ -228,32 +168,18 @@ impl EventRouter { ApiEventType::WebHookStateChanged { webhook, enabled } => { LoggerEvent::Defguard(DefguardEvent::WebHookStateChanged { webhook, enabled }) } - ApiEventType::AuthenticationKeyAdded { - key_id, - key_name, - key_type, - } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyAdded { - key_id, - key_name, - key_type, - }), - ApiEventType::AuthenticationKeyRemoved { - key_id, - key_name, - key_type, - } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRemoved { - key_id, - key_name, - key_type, - }), + ApiEventType::AuthenticationKeyAdded { key } => { + LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyAdded { key }) + } + ApiEventType::AuthenticationKeyRemoved { key } => { + LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRemoved { key }) + } ApiEventType::AuthenticationKeyRenamed { - key_id, - key_type, + key, old_name, new_name, } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRenamed { - key_id, - key_type, + key, old_name, new_name, }), diff --git a/crates/defguard_event_router/src/lib.rs b/crates/defguard_event_router/src/lib.rs index bd4f4e013..c2c1b73de 100644 --- a/crates/defguard_event_router/src/lib.rs +++ b/crates/defguard_event_router/src/lib.rs @@ -148,7 +148,7 @@ impl EventRouter { }, }; - debug!("Received event: {event:?}"); + debug!("Received event"); // Route the event to the appropriate handler match event { From 32f41a437b59b872546c7e0c4c67294dc322ae12 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 12:28:45 +0200 Subject: [PATCH 15/23] Don't store secrets in metadata --- .../src/db/models/audit_log/metadata.rs | 160 ++++++++++++++++-- .../src/enterprise/db/models/api_tokens.rs | 2 +- crates/defguard_event_logger/src/lib.rs | 62 +++++-- crates/defguard_event_logger/src/message.rs | 5 +- 4 files changed, 190 insertions(+), 39 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 94e881532..136708b46 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -1,10 +1,17 @@ +use chrono::NaiveDateTime; + use crate::{ db::{ - models::{authentication_key::AuthenticationKey, oauth2client::OAuth2Client}, + models::{ + authentication_key::{AuthenticationKey, AuthenticationKeyType}, + oauth2client::OAuth2Client, + }, Device, Group, Id, MFAMethod, User, WebAuthn, WebHook, WireguardNetwork, }, enterprise::db::models::{ - api_tokens::ApiToken, audit_stream::AuditStream, openid_provider::OpenIdProvider, + api_tokens::ApiToken, + audit_stream::{AuditStream, AuditStreamType}, + openid_provider::{DirectorySyncTarget, DirectorySyncUserBehavior, OpenIdProvider}, }, }; @@ -52,18 +59,18 @@ pub struct UserModifiedMetadata { #[derive(Serialize)] pub struct MfaSecurityKeyMetadata { - pub key: WebAuthnMetadata, + pub key: WebAuthnNoSecrets, } // Avoid storing secrets in metadata #[derive(Serialize)] -pub struct WebAuthnMetadata { +pub struct WebAuthnNoSecrets { pub id: Id, pub user_id: Id, pub name: String, } -impl From> for WebAuthnMetadata { +impl From> for WebAuthnNoSecrets { fn from(value: WebAuthn) -> Self { Self { id: value.id, @@ -75,13 +82,30 @@ impl From> for WebAuthnMetadata { #[derive(Serialize)] pub struct AuditStreamMetadata { - pub stream: AuditStream, + pub stream: AuditStreamNoSecrets, +} + +#[derive(Serialize)] +pub struct AuditStreamNoSecrets { + pub id: Id, + pub name: String, + pub stream_type: AuditStreamType, +} + +impl From> for AuditStreamNoSecrets { + fn from(value: AuditStream) -> Self { + Self { + id: value.id, + name: value.name, + stream_type: value.stream_type, + } + } } #[derive(Serialize)] pub struct AuditStreamModifiedMetadata { - pub before: AuditStream, - pub after: AuditStream, + pub before: AuditStreamNoSecrets, + pub after: AuditStreamNoSecrets, } #[derive(Serialize)] @@ -121,37 +145,118 @@ pub struct VpnLocationModifiedMetadata { #[derive(Serialize)] pub struct ApiTokenMetadata { pub owner: User, - pub token: ApiToken, + pub token: ApiTokenNoSecrets, +} + +#[derive(Serialize)] +pub struct ApiTokenNoSecrets { + id: Id, + pub user_id: Id, + pub created_at: NaiveDateTime, + pub name: String, +} + +impl From> for ApiTokenNoSecrets { + fn from(value: ApiToken) -> Self { + Self { + id: value.id, + user_id: value.user_id, + created_at: value.created_at, + name: value.name, + } + } } #[derive(Serialize)] pub struct ApiTokenRenamedMetadata { pub owner: User, - pub token: ApiToken, + pub token: ApiTokenNoSecrets, pub old_name: String, pub new_name: String, } #[derive(Serialize)] pub struct OpenIdAppMetadata { - pub app: OAuth2Client, + pub app: OAuth2ClientNoSecrets, +} + +#[derive(Serialize)] +pub struct OAuth2ClientNoSecrets { + pub id: Id, + pub client_id: String, // unique + pub redirect_uri: Vec, + pub scope: Vec, + pub name: String, + pub enabled: bool, +} + +impl From> for OAuth2ClientNoSecrets { + fn from(value: OAuth2Client) -> Self { + Self { + id: value.id, + client_id: value.client_id, + redirect_uri: value.redirect_uri, + scope: value.scope, + name: value.name, + enabled: value.enabled, + } + } } #[derive(Serialize)] pub struct OpenIdAppModifiedMetadata { - pub before: OAuth2Client, - pub after: OAuth2Client, + pub before: OAuth2ClientNoSecrets, + pub after: OAuth2ClientNoSecrets, } #[derive(Serialize)] pub struct OpenIdAppStateChangedMetadata { - pub app: OAuth2Client, + pub app: OAuth2ClientNoSecrets, pub enabled: bool, } #[derive(Serialize)] pub struct OpenIdProviderMetadata { - pub provider: OpenIdProvider, + pub provider: OpenIdProviderNoSecrets, +} + +#[derive(Serialize)] +pub struct OpenIdProviderNoSecrets { + pub id: Id, + pub name: String, + pub base_url: String, + pub client_id: String, + pub display_name: Option, + pub google_service_account_email: Option, + pub admin_email: Option, + pub directory_sync_enabled: bool, + pub directory_sync_interval: i32, + pub directory_sync_user_behavior: DirectorySyncUserBehavior, + pub directory_sync_admin_behavior: DirectorySyncUserBehavior, + pub directory_sync_target: DirectorySyncTarget, + pub okta_dirsync_client_id: Option, + pub directory_sync_group_match: Vec, +} + +impl From> for OpenIdProviderNoSecrets { + fn from(value: OpenIdProvider) -> Self { + Self { + id: value.id, + name: value.name, + base_url: value.base_url, + client_id: value.client_id, + display_name: value.display_name, + google_service_account_email: value.google_service_account_email, + admin_email: value.admin_email, + directory_sync_enabled: value.directory_sync_enabled, + directory_sync_interval: value.directory_sync_interval, + directory_sync_user_behavior: value.directory_sync_user_behavior, + directory_sync_admin_behavior: value.directory_sync_admin_behavior, + directory_sync_target: value.directory_sync_target, + okta_dirsync_client_id: value.okta_dirsync_client_id, + directory_sync_group_match: value.directory_sync_group_match, + } + } } #[derive(Serialize)] @@ -196,12 +301,33 @@ pub struct WebHookStateChangedMetadata { #[derive(Serialize)] pub struct AuthenticationKeyMetadata { - pub key: AuthenticationKey, + pub key: AuthenticationKeyNoSecrets, +} + +#[derive(Serialize)] +pub struct AuthenticationKeyNoSecrets { + pub id: Id, + pub yubikey_id: Option, + pub name: Option, + pub user_id: Id, + pub key_type: AuthenticationKeyType, +} + +impl From> for AuthenticationKeyNoSecrets { + fn from(value: AuthenticationKey) -> Self { + Self { + id: value.id, + yubikey_id: value.yubikey_id, + name: value.name, + user_id: value.user_id, + key_type: value.key_type, + } + } } #[derive(Serialize)] pub struct AuthenticationKeyRenamedMetadata { - pub key: AuthenticationKey, + pub key: AuthenticationKeyNoSecrets, pub old_name: Option, pub new_name: Option, } diff --git a/crates/defguard_core/src/enterprise/db/models/api_tokens.rs b/crates/defguard_core/src/enterprise/db/models/api_tokens.rs index 62bd31375..262c2361c 100644 --- a/crates/defguard_core/src/enterprise/db/models/api_tokens.rs +++ b/crates/defguard_core/src/enterprise/db/models/api_tokens.rs @@ -7,7 +7,7 @@ use crate::db::{Id, NoId}; #[derive(Clone, Deserialize, Model, Serialize)] #[table(api_token)] pub struct ApiToken { - id: I, + pub id: I, pub user_id: Id, pub created_at: NaiveDateTime, pub name: String, diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index ad8f95980..68ae41a88 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -130,11 +130,13 @@ pub async fn run_event_logger( ), DefguardEvent::AuthenticationKeyAdded { key } => ( EventType::AuthenticationKeyAdded, - serde_json::to_value(AuthenticationKeyMetadata { key }).ok(), + serde_json::to_value(AuthenticationKeyMetadata { key: key.into() }) + .ok(), ), DefguardEvent::AuthenticationKeyRemoved { key } => ( EventType::AuthenticationKeyRemoved, - serde_json::to_value(AuthenticationKeyMetadata { key }).ok(), + serde_json::to_value(AuthenticationKeyMetadata { key: key.into() }) + .ok(), ), DefguardEvent::AuthenticationKeyRenamed { key, @@ -143,7 +145,7 @@ pub async fn run_event_logger( } => ( EventType::AuthenticationKeyRenamed, serde_json::to_value(AuthenticationKeyRenamedMetadata { - key, + key: key.into(), old_name, new_name, }) @@ -151,11 +153,19 @@ pub async fn run_event_logger( ), DefguardEvent::ApiTokenAdded { owner, token } => ( EventType::ApiTokenAdded, - serde_json::to_value(ApiTokenMetadata { owner, token }).ok(), + serde_json::to_value(ApiTokenMetadata { + owner, + token: token.into(), + }) + .ok(), ), DefguardEvent::ApiTokenRemoved { owner, token } => ( EventType::ApiTokenRemoved, - serde_json::to_value(ApiTokenMetadata { owner, token }).ok(), + serde_json::to_value(ApiTokenMetadata { + owner, + token: token.into(), + }) + .ok(), ), DefguardEvent::ApiTokenRenamed { owner, @@ -166,7 +176,7 @@ pub async fn run_event_logger( EventType::ApiTokenRenamed, serde_json::to_value(ApiTokenRenamedMetadata { owner, - token, + token: token.into(), old_name, new_name, }) @@ -222,32 +232,41 @@ pub async fn run_event_logger( ), DefguardEvent::OpenIdAppAdded { app } => ( EventType::OpenIdAppAdded, - serde_json::to_value(OpenIdAppMetadata { app }).ok(), + serde_json::to_value(OpenIdAppMetadata { app: app.into() }).ok(), ), DefguardEvent::OpenIdAppRemoved { app } => ( EventType::OpenIdAppRemoved, - serde_json::to_value(OpenIdAppMetadata { app }).ok(), + serde_json::to_value(OpenIdAppMetadata { app: app.into() }).ok(), ), DefguardEvent::OpenIdAppModified { before, after } => ( EventType::OpenIdAppModified, - serde_json::to_value(OpenIdAppModifiedMetadata { before, after }) - .ok(), + serde_json::to_value(OpenIdAppModifiedMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), ), DefguardEvent::OpenIdAppStateChanged { app, enabled } => ( EventType::OpenIdAppStateChanged, serde_json::to_value(OpenIdAppStateChangedMetadata { - app, + app: app.into(), enabled, }) .ok(), ), DefguardEvent::OpenIdProviderModified { provider } => ( EventType::OpenIdProviderModified, - serde_json::to_value(OpenIdProviderMetadata { provider }).ok(), + serde_json::to_value(OpenIdProviderMetadata { + provider: provider.into(), + }) + .ok(), ), DefguardEvent::OpenIdProviderRemoved { provider } => ( EventType::OpenIdProviderRemoved, - serde_json::to_value(OpenIdProviderMetadata { provider }).ok(), + serde_json::to_value(OpenIdProviderMetadata { + provider: provider.into(), + }) + .ok(), ), DefguardEvent::SettingsUpdated => (EventType::SettingsUpdated, None), DefguardEvent::SettingsUpdatedPartial => { @@ -258,16 +277,25 @@ pub async fn run_event_logger( } DefguardEvent::AuditStreamCreated { stream } => ( EventType::AuditStreamCreated, - serde_json::to_value(AuditStreamMetadata { stream }).ok(), + serde_json::to_value(AuditStreamMetadata { + stream: stream.into(), + }) + .ok(), ), DefguardEvent::AuditStreamRemoved { stream } => ( EventType::AuditStreamRemoved, - serde_json::to_value(AuditStreamMetadata { stream }).ok(), + serde_json::to_value(AuditStreamMetadata { + stream: stream.into(), + }) + .ok(), ), DefguardEvent::AuditStreamModified { before, after } => ( EventType::AuditStreamModified, - serde_json::to_value(AuditStreamModifiedMetadata { before, after }) - .ok(), + serde_json::to_value(AuditStreamModifiedMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), ), DefguardEvent::GroupsBulkAssigned { users, groups } => ( EventType::GroupsBulkAssigned, diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index e67747531..b33a52512 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -3,10 +3,7 @@ use std::net::IpAddr; use defguard_core::{ db::{ - models::{ - authentication_key::{AuthenticationKey, AuthenticationKeyType}, - oauth2client::OAuth2Client, - }, + models::{authentication_key::AuthenticationKey, oauth2client::OAuth2Client}, Device, Group, Id, MFAMethod, User, WebAuthn, WebHook, WireguardNetwork, }, enterprise::db::models::{ From d27a1cc5ba8fc3a181df63a671e28d7d4c0a72b1 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 13:01:38 +0200 Subject: [PATCH 16/23] Implement UserNoSecrets metadata struct --- .../src/db/models/audit_log/metadata.rs | 67 ++++++++++++++---- crates/defguard_core/src/events.rs | 5 +- crates/defguard_event_logger/src/lib.rs | 69 ++++++++++++++----- 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/crates/defguard_core/src/db/models/audit_log/metadata.rs b/crates/defguard_core/src/db/models/audit_log/metadata.rs index 136708b46..141b7cc48 100644 --- a/crates/defguard_core/src/db/models/audit_log/metadata.rs +++ b/crates/defguard_core/src/db/models/audit_log/metadata.rs @@ -20,15 +20,56 @@ pub struct MfaLoginMetadata { pub mfa_method: MFAMethod, } +#[derive(Serialize)] +pub struct UserNoSecrets { + pub id: Id, + pub username: String, + pub last_name: String, + pub first_name: String, + pub email: String, + pub phone: Option, + pub mfa_enabled: bool, + pub is_active: bool, + pub from_ldap: bool, + pub ldap_pass_randomized: bool, + pub ldap_rdn: Option, + pub openid_sub: Option, + pub totp_enabled: bool, + pub email_mfa_enabled: bool, + pub mfa_method: MFAMethod, +} + +impl From> for UserNoSecrets { + fn from(value: User) -> Self { + Self { + id: value.id, + username: value.username, + last_name: value.last_name, + first_name: value.first_name, + email: value.email, + phone: value.phone, + mfa_enabled: value.mfa_enabled, + is_active: value.is_active, + from_ldap: value.from_ldap, + ldap_pass_randomized: value.ldap_pass_randomized, + ldap_rdn: value.ldap_rdn, + openid_sub: value.openid_sub, + totp_enabled: value.totp_enabled, + email_mfa_enabled: value.email_mfa_enabled, + mfa_method: value.mfa_method, + } + } +} + #[derive(Serialize)] pub struct DeviceMetadata { - pub owner: User, + pub owner: UserNoSecrets, pub device: Device, } #[derive(Serialize)] pub struct DeviceModifiedMetadata { - pub owner: User, + pub owner: UserNoSecrets, pub before: Device, pub after: Device, } @@ -48,13 +89,13 @@ pub struct NetworkDeviceModifiedMetadata { #[derive(Serialize)] pub struct UserMetadata { - pub user: User, + pub user: UserNoSecrets, } #[derive(Serialize)] pub struct UserModifiedMetadata { - pub before: User, - pub after: User, + pub before: UserNoSecrets, + pub after: UserNoSecrets, } #[derive(Serialize)] @@ -128,7 +169,7 @@ pub struct EnrollmentDeviceAddedMetadata { #[derive(Serialize)] pub struct EnrollmentTokenMetadata { - pub user: User, + pub user: UserNoSecrets, } #[derive(Serialize)] @@ -144,7 +185,7 @@ pub struct VpnLocationModifiedMetadata { #[derive(Serialize)] pub struct ApiTokenMetadata { - pub owner: User, + pub owner: UserNoSecrets, pub token: ApiTokenNoSecrets, } @@ -169,7 +210,7 @@ impl From> for ApiTokenNoSecrets { #[derive(Serialize)] pub struct ApiTokenRenamedMetadata { - pub owner: User, + pub owner: UserNoSecrets, pub token: ApiTokenNoSecrets, pub old_name: String, pub new_name: String, @@ -261,7 +302,7 @@ impl From> for OpenIdProviderNoSecrets { #[derive(Serialize)] pub struct GroupsBulkAssignedMetadata { - pub users: Vec>, + pub users: Vec, pub groups: Vec>, } @@ -279,7 +320,7 @@ pub struct GroupModifiedMetadata { #[derive(Serialize)] pub struct GroupAssignedMetadata { pub group: Group, - pub user: User, + pub user: UserNoSecrets, } #[derive(Serialize)] @@ -334,15 +375,15 @@ pub struct AuthenticationKeyRenamedMetadata { #[derive(Serialize)] pub struct PasswordChangedByAdminMetadata { - pub user: User, + pub user: UserNoSecrets, } #[derive(Serialize)] pub struct PasswordResetMetadata { - pub user: User, + pub user: UserNoSecrets, } #[derive(Serialize)] pub struct ClientConfigurationTokenMetadata { - pub user: User, + pub user: UserNoSecrets, } diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 7ce65f00d..01b7dab56 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -2,10 +2,7 @@ use std::net::IpAddr; use crate::{ db::{ - models::{ - authentication_key::{AuthenticationKey, AuthenticationKeyType}, - oauth2client::OAuth2Client, - }, + models::{authentication_key::AuthenticationKey, oauth2client::OAuth2Client}, Device, Group, Id, MFAMethod, User, WebAuthn, WebHook, WireguardNetwork, }, enterprise::db::models::{ diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 68ae41a88..084e349c4 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -88,11 +88,19 @@ pub async fn run_event_logger( DefguardEvent::UserLogout => (EventType::UserLogout, None), DefguardEvent::UserDeviceAdded { owner, device } => ( EventType::DeviceAdded, - serde_json::to_value(DeviceMetadata { owner, device }).ok(), + serde_json::to_value(DeviceMetadata { + owner: owner.into(), + device, + }) + .ok(), ), DefguardEvent::UserDeviceRemoved { owner, device } => ( EventType::DeviceRemoved, - serde_json::to_value(DeviceMetadata { owner, device }).ok(), + serde_json::to_value(DeviceMetadata { + owner: owner.into(), + device, + }) + .ok(), ), DefguardEvent::UserDeviceModified { owner, @@ -101,7 +109,7 @@ pub async fn run_event_logger( } => ( EventType::DeviceModified, serde_json::to_value(DeviceModifiedMetadata { - owner, + owner: owner.into(), before, after, }) @@ -111,7 +119,10 @@ pub async fn run_event_logger( DefguardEvent::PasswordChanged => (EventType::PasswordChanged, None), DefguardEvent::PasswordChangedByAdmin { user } => ( EventType::PasswordChangedByAdmin, - serde_json::to_value(PasswordChangedByAdminMetadata { user }).ok(), + serde_json::to_value(PasswordChangedByAdminMetadata { + user: user.into(), + }) + .ok(), ), DefguardEvent::MfaDisabled => (EventType::MfaDisabled, None), DefguardEvent::MfaTotpEnabled => (EventType::MfaTotpEnabled, None), @@ -154,7 +165,7 @@ pub async fn run_event_logger( DefguardEvent::ApiTokenAdded { owner, token } => ( EventType::ApiTokenAdded, serde_json::to_value(ApiTokenMetadata { - owner, + owner: owner.into(), token: token.into(), }) .ok(), @@ -162,7 +173,7 @@ pub async fn run_event_logger( DefguardEvent::ApiTokenRemoved { owner, token } => ( EventType::ApiTokenRemoved, serde_json::to_value(ApiTokenMetadata { - owner, + owner: owner.into(), token: token.into(), }) .ok(), @@ -175,7 +186,7 @@ pub async fn run_event_logger( } => ( EventType::ApiTokenRenamed, serde_json::to_value(ApiTokenRenamedMetadata { - owner, + owner: owner.into(), token: token.into(), old_name, new_name, @@ -184,15 +195,19 @@ pub async fn run_event_logger( ), DefguardEvent::UserAdded { user } => ( EventType::UserAdded, - serde_json::to_value(UserMetadata { user }).ok(), + serde_json::to_value(UserMetadata { user: user.into() }).ok(), ), DefguardEvent::UserRemoved { user } => ( EventType::UserRemoved, - serde_json::to_value(UserMetadata { user }).ok(), + serde_json::to_value(UserMetadata { user: user.into() }).ok(), ), DefguardEvent::UserModified { before, after } => ( EventType::UserModified, - serde_json::to_value(UserModifiedMetadata { before, after }).ok(), + serde_json::to_value(UserModifiedMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), ), DefguardEvent::NetworkDeviceAdded { device, location } => ( EventType::NetworkDeviceAdded, @@ -299,8 +314,11 @@ pub async fn run_event_logger( ), DefguardEvent::GroupsBulkAssigned { users, groups } => ( EventType::GroupsBulkAssigned, - serde_json::to_value(GroupsBulkAssignedMetadata { users, groups }) - .ok(), + serde_json::to_value(GroupsBulkAssignedMetadata { + users: users.into_iter().map(Into::into).collect(), + groups, + }) + .ok(), ), DefguardEvent::GroupAdded { group } => ( EventType::GroupAdded, @@ -316,11 +334,19 @@ pub async fn run_event_logger( ), DefguardEvent::GroupMemberAdded { group, user } => ( EventType::GroupMemberAdded, - serde_json::to_value(GroupAssignedMetadata { group, user }).ok(), + serde_json::to_value(GroupAssignedMetadata { + group, + user: user.into(), + }) + .ok(), ), DefguardEvent::GroupMemberRemoved { group, user } => ( EventType::GroupMemberRemoved, - serde_json::to_value(GroupAssignedMetadata { group, user }).ok(), + serde_json::to_value(GroupAssignedMetadata { + group, + user: user.into(), + }) + .ok(), ), DefguardEvent::WebHookAdded { webhook } => ( EventType::WebHookAdded, @@ -345,16 +371,20 @@ pub async fn run_event_logger( ), DefguardEvent::PasswordReset { user } => ( EventType::PasswordReset, - serde_json::to_value(PasswordResetMetadata { user }).ok(), + serde_json::to_value(PasswordResetMetadata { user: user.into() }) + .ok(), ), DefguardEvent::ClientConfigurationTokenAdded { user } => ( EventType::ClientConfigurationTokenAdded, - serde_json::to_value(ClientConfigurationTokenMetadata { user }) - .ok(), + serde_json::to_value(ClientConfigurationTokenMetadata { + user: user.into(), + }) + .ok(), ), DefguardEvent::EnrollmentTokenAdded { user } => ( EventType::EnrollmentTokenAdded, - serde_json::to_value(EnrollmentTokenMetadata { user }).ok(), + serde_json::to_value(EnrollmentTokenMetadata { user: user.into() }) + .ok(), ), }; (module, event_type, metadata) @@ -431,7 +461,8 @@ pub async fn run_event_logger( } EnrollmentEvent::TokenAdded { user } => ( EventType::EnrollmentTokenAdded, - serde_json::to_value(EnrollmentTokenMetadata { user }).ok(), + serde_json::to_value(EnrollmentTokenMetadata { user: user.into() }) + .ok(), ), }; (module, event_type, metadata) From 8380b2611d9ed9843069f854eb1915e71df2f934 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 13:17:26 +0200 Subject: [PATCH 17/23] Update sqlx query data --- ...a40e2e9833486425dc105a0411bd634a080391e41f431f966c17.json} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .sqlx/{query-0d0ed874821849ae07a9f49f17600b2a4cbedb33babd5b9fc908ec17d3f882e2.json => query-770fcf951f69a40e2e9833486425dc105a0411bd634a080391e41f431f966c17.json} (85%) diff --git a/.sqlx/query-0d0ed874821849ae07a9f49f17600b2a4cbedb33babd5b9fc908ec17d3f882e2.json b/.sqlx/query-770fcf951f69a40e2e9833486425dc105a0411bd634a080391e41f431f966c17.json similarity index 85% rename from .sqlx/query-0d0ed874821849ae07a9f49f17600b2a4cbedb33babd5b9fc908ec17d3f882e2.json rename to .sqlx/query-770fcf951f69a40e2e9833486425dc105a0411bd634a080391e41f431f966c17.json index c7a2ba03b..418350329 100644 --- a/.sqlx/query-0d0ed874821849ae07a9f49f17600b2a4cbedb33babd5b9fc908ec17d3f882e2.json +++ b/.sqlx/query-770fcf951f69a40e2e9833486425dc105a0411bd634a080391e41f431f966c17.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, base_url, client_id, client_secret, display_name, google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled, \n directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", directory_sync_target \"directory_sync_target: DirectorySyncTarget\", okta_private_jwk, okta_dirsync_client_id, directory_sync_group_match FROM openidprovider WHERE name = $1", + "query": "SELECT id, name, base_url, client_id, client_secret, display_name, google_service_account_key, google_service_account_email, admin_email, directory_sync_enabled,\n directory_sync_interval, directory_sync_user_behavior \"directory_sync_user_behavior: DirectorySyncUserBehavior\", directory_sync_admin_behavior \"directory_sync_admin_behavior: DirectorySyncUserBehavior\", directory_sync_target \"directory_sync_target: DirectorySyncTarget\", okta_private_jwk, okta_dirsync_client_id, directory_sync_group_match FROM openidprovider WHERE name = $1", "describe": { "columns": [ { @@ -147,5 +147,5 @@ false ] }, - "hash": "0d0ed874821849ae07a9f49f17600b2a4cbedb33babd5b9fc908ec17d3f882e2" + "hash": "770fcf951f69a40e2e9833486425dc105a0411bd634a080391e41f431f966c17" } From c8d64e2883cb63bd774045ee429fab28923ebd11 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 13:18:12 +0200 Subject: [PATCH 18/23] Remove skip_serializing marker --- crates/defguard_core/src/db/models/user.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/defguard_core/src/db/models/user.rs b/crates/defguard_core/src/db/models/user.rs index a7f391a09..e6db06197 100644 --- a/crates/defguard_core/src/db/models/user.rs +++ b/crates/defguard_core/src/db/models/user.rs @@ -93,7 +93,6 @@ pub struct UserDiagnostic { pub struct User { pub id: I, pub username: String, - #[serde(skip_serializing)] pub(crate) password_hash: Option, pub last_name: String, pub first_name: String, From 2cc80c248f8d4aa86ab3d52ec3ad8107b75f0423 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 14:28:57 +0200 Subject: [PATCH 19/23] Box event enums to avoid big size differences between variants --- .../src/enterprise/handlers/api_tokens.rs | 10 +- .../src/enterprise/handlers/audit_stream.rs | 8 +- .../enterprise/handlers/openid_providers.rs | 6 +- crates/defguard_core/src/events.rs | 11 +- .../src/grpc/desktop_client_mfa.rs | 21 +-- crates/defguard_core/src/grpc/enrollment.rs | 2 +- crates/defguard_core/src/grpc/gateway/mod.rs | 1 - .../defguard_core/src/grpc/password_reset.rs | 2 +- crates/defguard_core/src/handlers/auth.rs | 50 +++---- crates/defguard_core/src/handlers/group.rs | 14 +- .../src/handlers/network_devices.rs | 8 +- .../src/handlers/openid_clients.rs | 14 +- crates/defguard_core/src/handlers/settings.rs | 6 +- .../src/handlers/ssh_authorized_keys.rs | 8 +- crates/defguard_core/src/handlers/user.rs | 20 +-- crates/defguard_core/src/handlers/webhooks.rs | 12 +- .../defguard_core/src/handlers/wireguard.rs | 26 ++-- crates/defguard_event_logger/src/lib.rs | 6 +- crates/defguard_event_logger/src/message.rs | 10 +- crates/defguard_event_router/src/events.rs | 1 - .../defguard_event_router/src/handlers/api.rs | 126 +++++++++--------- .../src/handlers/bidi.rs | 30 +++-- .../src/handlers/grpc.rs | 7 +- .../src/handlers/internal.rs | 5 +- 24 files changed, 204 insertions(+), 200 deletions(-) diff --git a/crates/defguard_core/src/enterprise/handlers/api_tokens.rs b/crates/defguard_core/src/enterprise/handlers/api_tokens.rs index 7b2e1291b..808fd114b 100644 --- a/crates/defguard_core/src/enterprise/handlers/api_tokens.rs +++ b/crates/defguard_core/src/enterprise/handlers/api_tokens.rs @@ -69,7 +69,7 @@ pub async fn add_api_token( if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { appstate.emit_event(ApiEvent { context, - event: ApiEventType::ApiTokenAdded { owner, token }, + event: Box::new(ApiEventType::ApiTokenAdded { owner, token }), })?; } Ok(ApiResponse { @@ -117,10 +117,10 @@ pub async fn delete_api_token( if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { appstate.emit_event(ApiEvent { context, - event: ApiEventType::ApiTokenRemoved { + event: Box::new(ApiEventType::ApiTokenRemoved { owner, token: token.clone(), - }, + }), })?; } info!( @@ -165,12 +165,12 @@ pub async fn rename_api_token( if let Some(owner) = User::find_by_id(&appstate.pool, token.user_id).await? { appstate.emit_event(ApiEvent { context, - event: ApiEventType::ApiTokenRenamed { + event: Box::new(ApiEventType::ApiTokenRenamed { owner, token: token.clone(), old_name, new_name, - }, + }), })?; } info!( diff --git a/crates/defguard_core/src/enterprise/handlers/audit_stream.rs b/crates/defguard_core/src/enterprise/handlers/audit_stream.rs index 9d685ad1c..e920801ee 100644 --- a/crates/defguard_core/src/enterprise/handlers/audit_stream.rs +++ b/crates/defguard_core/src/enterprise/handlers/audit_stream.rs @@ -60,7 +60,7 @@ pub async fn create_audit_stream( info!("User {session_username} created audit stream"); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuditStreamCreated { stream }, + event: Box::new(ApiEventType::AuditStreamCreated { stream }), })?; Ok(ApiResponse { json: json!({}), @@ -93,10 +93,10 @@ pub async fn modify_audit_stream( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuditStreamModified { + event: Box::new(ApiEventType::AuditStreamModified { before, after: stream, - }, + }), })?; debug!("AuditStreamModified api event sent"); return Ok(ApiResponse::default()); @@ -120,7 +120,7 @@ pub async fn delete_audit_stream( stream.clone().delete(&appstate.pool).await?; appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuditStreamRemoved { stream }, + event: Box::new(ApiEventType::AuditStreamRemoved { stream }), })?; } else { return Err(crate::error::WebError::ObjectNotFound(format!( diff --git a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs index 4b6c1fe53..92e63f292 100644 --- a/crates/defguard_core/src/enterprise/handlers/openid_providers.rs +++ b/crates/defguard_core/src/enterprise/handlers/openid_providers.rs @@ -160,9 +160,9 @@ pub async fn add_openid_provider( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdProviderModified { + event: Box::new(ApiEventType::OpenIdProviderModified { provider: new_provider, - }, + }), })?; Ok(ApiResponse { @@ -222,7 +222,7 @@ pub async fn delete_openid_provider( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdProviderRemoved { provider }, + event: Box::new(ApiEventType::OpenIdProviderRemoved { provider }), })?; Ok(ApiResponse { json: json!({}), diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 01b7dab56..0b4719663 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -251,7 +251,7 @@ pub enum ApiEventType { /// Events from Web API pub struct ApiEvent { pub context: ApiRequestContext, - pub event: ApiEventType, + pub event: Box, } /// Events from gRPC server @@ -300,18 +300,17 @@ impl BidiRequestContext { #[derive(Debug)] pub struct BidiStreamEvent { pub context: BidiRequestContext, - pub event: BidiStreamEventType, + pub event: Box, } /// Wrapper enum for different types of events emitted by the bidi stream. /// /// Each variant represents a separate gRPC service that's part of the bi-directional communications server. -#[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum BidiStreamEventType { - Enrollment(EnrollmentEvent), - PasswordReset(PasswordResetEvent), - DesktopClientMfa(DesktopClientMfaEvent), + Enrollment(Box), + PasswordReset(Box), + DesktopClientMfa(Box), } #[derive(Debug)] diff --git a/crates/defguard_core/src/grpc/desktop_client_mfa.rs b/crates/defguard_core/src/grpc/desktop_client_mfa.rs index f0909651c..0d545e8bc 100644 --- a/crates/defguard_core/src/grpc/desktop_client_mfa.rs +++ b/crates/defguard_core/src/grpc/desktop_client_mfa.rs @@ -28,7 +28,6 @@ use crate::{ const CLIENT_SESSION_TIMEOUT: u64 = 60 * 5; // 10 minutes #[derive(Debug, Error)] -#[allow(clippy::large_enum_variant)] pub enum ClientMfaServerError { #[error("gRPC event channel error: {0}")] BidiEventChannelError(#[from] SendError), @@ -251,13 +250,13 @@ impl ClientMfaServer { error!("Provided TOTP code is not valid"); self.emit_event(BidiStreamEvent { context, - event: BidiStreamEventType::DesktopClientMfa( + event: Box::new(BidiStreamEventType::DesktopClientMfa(Box::new( DesktopClientMfaEvent::Failed { location: location.clone(), device: device.clone(), method: (*method).into(), }, - ), + ))), })?; return Err(Status::unauthenticated("unauthorized")); } @@ -267,13 +266,13 @@ impl ClientMfaServer { error!("Provided email code is not valid"); self.emit_event(BidiStreamEvent { context, - event: BidiStreamEventType::DesktopClientMfa( + event: Box::new(BidiStreamEventType::DesktopClientMfa(Box::new( DesktopClientMfaEvent::Failed { location: location.clone(), device: device.clone(), method: (*method).into(), }, - ), + ))), })?; return Err(Status::unauthenticated("unauthorized")); } @@ -334,11 +333,13 @@ impl ClientMfaServer { ); self.emit_event(BidiStreamEvent { context, - event: BidiStreamEventType::DesktopClientMfa(DesktopClientMfaEvent::Connected { - location: location.clone(), - device: device.clone(), - method: (*method).into(), - }), + event: Box::new(BidiStreamEventType::DesktopClientMfa(Box::new( + DesktopClientMfaEvent::Connected { + location: location.clone(), + device: device.clone(), + method: (*method).into(), + }, + ))), })?; // remove login session from map diff --git a/crates/defguard_core/src/grpc/enrollment.rs b/crates/defguard_core/src/grpc/enrollment.rs index c57ccc382..03d9a49ee 100644 --- a/crates/defguard_core/src/grpc/enrollment.rs +++ b/crates/defguard_core/src/grpc/enrollment.rs @@ -104,7 +104,7 @@ impl EnrollmentServer { ) -> Result<(), SendError> { let event = BidiStreamEvent { context, - event: BidiStreamEventType::Enrollment(event), + event: Box::new(BidiStreamEventType::Enrollment(Box::new(event))), }; self.bidi_event_tx.send(event) diff --git a/crates/defguard_core/src/grpc/gateway/mod.rs b/crates/defguard_core/src/grpc/gateway/mod.rs index 2717838c9..ce8e80c16 100644 --- a/crates/defguard_core/src/grpc/gateway/mod.rs +++ b/crates/defguard_core/src/grpc/gateway/mod.rs @@ -55,7 +55,6 @@ pub fn send_multiple_wireguard_events(events: Vec, wg_tx: &Sender< } #[derive(Debug, Error)] -#[allow(clippy::large_enum_variant)] pub enum GatewayServerError { #[error("Failed to acquire lock on VPN client state map")] ClientStateMutexError, diff --git a/crates/defguard_core/src/grpc/password_reset.rs b/crates/defguard_core/src/grpc/password_reset.rs index 987716c24..6799c91fc 100644 --- a/crates/defguard_core/src/grpc/password_reset.rs +++ b/crates/defguard_core/src/grpc/password_reset.rs @@ -83,7 +83,7 @@ impl PasswordResetServer { ) -> Result<(), SendError> { let event = BidiStreamEvent { context, - event: BidiStreamEventType::PasswordReset(event), + event: Box::new(BidiStreamEventType::PasswordReset(Box::new(event))), }; self.bidi_event_tx.send(event) diff --git a/crates/defguard_core/src/handlers/auth.rs b/crates/defguard_core/src/handlers/auth.rs index 903811d64..37c51e0ed 100644 --- a/crates/defguard_core/src/handlers/auth.rs +++ b/crates/defguard_core/src/handlers/auth.rs @@ -159,7 +159,7 @@ pub(crate) async fn authenticate( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserLoginFailed, + event: Box::new(ApiEventType::UserLoginFailed), })?; return Err(WebError::Authorization(err.to_string())); } @@ -174,7 +174,7 @@ pub(crate) async fn authenticate( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserLoginFailed, + event: Box::new(ApiEventType::UserLoginFailed), })?; return Err(WebError::Authorization(err.to_string())); } @@ -204,7 +204,7 @@ pub(crate) async fn authenticate( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserLoginFailed, + event: Box::new(ApiEventType::UserLoginFailed), })?; return Err(WebError::Authorization(err.to_string())); } @@ -218,7 +218,7 @@ pub(crate) async fn authenticate( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserLoginFailed, + event: Box::new(ApiEventType::UserLoginFailed), })?; return Err(WebError::Authorization(err.to_string())); } @@ -310,7 +310,7 @@ pub(crate) async fn authenticate( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserLogin, + event: Box::new(ApiEventType::UserLogin), })?; Ok(( @@ -355,7 +355,7 @@ pub async fn logout( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserLogout, + event: Box::new(ApiEventType::UserLogout), })?; Ok((cookies, ApiResponse::default())) @@ -397,7 +397,7 @@ pub async fn mfa_disable( user.disable_mfa(&appstate.pool).await?; appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaDisabled, + event: Box::new(ApiEventType::MfaDisabled), })?; info!("Disabled MFA for user {}", user.username); Ok(ApiResponse::default()) @@ -503,7 +503,7 @@ pub async fn webauthn_finish( info!("Finished Webauthn registration for user {}", user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaSecurityKeyAdded { key: webauthn }, + event: Box::new(ApiEventType::MfaSecurityKeyAdded { key: webauthn }), })?; Ok(ApiResponse { @@ -567,9 +567,9 @@ pub async fn webauthn_end( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserMfaLogin { + event: Box::new(ApiEventType::UserMfaLogin { mfa_method: MFAMethod::Webauthn, - }, + }), })?; if let Some(openid_cookie) = private_cookies.get(SIGN_IN_COOKIE_NAME) { @@ -614,9 +614,9 @@ pub async fn webauthn_end( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserMfaLoginFailed { + event: Box::new(ApiEventType::UserMfaLoginFailed { mfa_method: MFAMethod::Webauthn, - }, + }), })?; } } @@ -663,7 +663,7 @@ pub async fn totp_enable( info!("Enabled TOTP for user {}", user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaTotpEnabled, + event: Box::new(ApiEventType::MfaTotpEnabled), })?; Ok(ApiResponse { json: json!(recovery_codes), @@ -687,7 +687,7 @@ pub async fn totp_disable( info!("Disabled TOTP for user {}", user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaTotpDisabled, + event: Box::new(ApiEventType::MfaTotpDisabled), })?; Ok(ApiResponse::default()) } @@ -720,9 +720,9 @@ pub async fn totp_code( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserMfaLogin { + event: Box::new(ApiEventType::UserMfaLogin { mfa_method: MFAMethod::OneTimePassword, - }, + }), })?; if let Some(openid_cookie) = private_cookies.get(SIGN_IN_COOKIE_NAME) { debug!("Found openid session cookie."); @@ -761,9 +761,9 @@ pub async fn totp_code( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserMfaLoginFailed { + event: Box::new(ApiEventType::UserMfaLoginFailed { mfa_method: MFAMethod::OneTimePassword, - }, + }), })?; Err(WebError::Authorization("Invalid TOTP code".into())) } @@ -819,7 +819,7 @@ pub async fn email_mfa_enable( info!("Enabled email MFA for user {}", user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaEmailEnabled, + event: Box::new(ApiEventType::MfaEmailEnabled), })?; Ok(ApiResponse { json: json!(recovery_codes), @@ -843,7 +843,7 @@ pub async fn email_mfa_disable( info!("Disabled email MFA for user {}", user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaEmailDisabled, + event: Box::new(ApiEventType::MfaEmailDisabled), })?; Ok(ApiResponse::default()) } @@ -895,9 +895,9 @@ pub async fn email_mfa_code( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserMfaLogin { + event: Box::new(ApiEventType::UserMfaLogin { mfa_method: MFAMethod::Email, - }, + }), })?; if let Some(openid_cookie) = private_cookies.get(SIGN_IN_COOKIE_NAME) { debug!("Found openid session cookie."); @@ -936,9 +936,9 @@ pub async fn email_mfa_code( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::UserMfaLoginFailed { + event: Box::new(ApiEventType::UserMfaLoginFailed { mfa_method: MFAMethod::Email, - }, + }), })?; Err(WebError::Authorization("Invalid email MFA code".into())) } @@ -978,7 +978,7 @@ pub async fn recovery_code( insecure_ip, user_agent.to_string(), ), - event: ApiEventType::RecoveryCodeUsed, + event: Box::new(ApiEventType::RecoveryCodeUsed), })?; if let Some(openid_cookie) = private_cookies.get(SIGN_IN_COOKIE_NAME) { debug!("Found OpenID session cookie."); diff --git a/crates/defguard_core/src/handlers/group.rs b/crates/defguard_core/src/handlers/group.rs index 471946e78..45be11372 100644 --- a/crates/defguard_core/src/handlers/group.rs +++ b/crates/defguard_core/src/handlers/group.rs @@ -127,7 +127,7 @@ pub(crate) async fn bulk_assign_to_groups( info!("Assigned {} groups to {} users.", groups.len(), users.len()); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupsBulkAssigned { users, groups }, + event: Box::new(ApiEventType::GroupsBulkAssigned { users, groups }), })?; Ok(ApiResponse { @@ -359,7 +359,7 @@ pub(crate) async fn create_group( info!("Created group {}", group_info.name); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupAdded { group }, + event: Box::new(ApiEventType::GroupAdded { group }), })?; Ok(ApiResponse { @@ -493,10 +493,10 @@ pub(crate) async fn modify_group( info!("Modified group {}", group.name); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupModified { + event: Box::new(ApiEventType::GroupModified { before, after: group, - }, + }), })?; Ok(ApiResponse::default()) } @@ -556,7 +556,7 @@ pub(crate) async fn delete_group( info!("Deleted group {name}"); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupRemoved { group }, + event: Box::new(ApiEventType::GroupRemoved { group }), })?; Ok(ApiResponse::default()) } else { @@ -609,7 +609,7 @@ pub(crate) async fn add_group_member( info!("Added user: {} to group: {}", user.username, group.name); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupMemberAdded { group, user }, + event: Box::new(ApiEventType::GroupMemberAdded { group, user }), })?; Ok(ApiResponse::default()) } else { @@ -672,7 +672,7 @@ pub(crate) async fn remove_group_member( info!("Removed user: {} from group: {}", user.username, group.name); appstate.emit_event(ApiEvent { context, - event: ApiEventType::GroupMemberRemoved { group, user }, + event: Box::new(ApiEventType::GroupMemberRemoved { group, user }), })?; Ok(ApiResponse { json: json!({}), diff --git a/crates/defguard_core/src/handlers/network_devices.rs b/crates/defguard_core/src/handlers/network_devices.rs index 569094742..cdd3ccb42 100644 --- a/crates/defguard_core/src/handlers/network_devices.rs +++ b/crates/defguard_core/src/handlers/network_devices.rs @@ -644,10 +644,10 @@ pub(crate) async fn add_network_device( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::NetworkDeviceAdded { + event: Box::new(ApiEventType::NetworkDeviceAdded { device, location: network, - }, + }), })?; Ok(ApiResponse { @@ -739,11 +739,11 @@ pub async fn modify_network_device( appstate.emit_event(ApiEvent { context, - event: ApiEventType::NetworkDeviceModified { + event: Box::new(ApiEventType::NetworkDeviceModified { before, after: device, location: device_network, - }, + }), })?; Ok(ApiResponse { json: json!(network_device_info), diff --git a/crates/defguard_core/src/handlers/openid_clients.rs b/crates/defguard_core/src/handlers/openid_clients.rs index e17556979..7d71e359b 100644 --- a/crates/defguard_core/src/handlers/openid_clients.rs +++ b/crates/defguard_core/src/handlers/openid_clients.rs @@ -33,9 +33,9 @@ pub async fn add_openid_client( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdAppAdded { + event: Box::new(ApiEventType::OpenIdAppAdded { app: client.clone(), - }, + }), })?; Ok(ApiResponse { json: json!(client), @@ -104,10 +104,10 @@ pub async fn change_openid_client( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdAppModified { + event: Box::new(ApiEventType::OpenIdAppModified { before, after: client, - }, + }), })?; StatusCode::OK } @@ -141,10 +141,10 @@ pub async fn change_openid_client_state( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdAppStateChanged { + event: Box::new(ApiEventType::OpenIdAppStateChanged { enabled: client.enabled, app: client, - }, + }), })?; StatusCode::OK } @@ -176,7 +176,7 @@ pub async fn delete_openid_client( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::OpenIdAppRemoved { app: client }, + event: Box::new(ApiEventType::OpenIdAppRemoved { app: client }), })?; StatusCode::OK } diff --git a/crates/defguard_core/src/handlers/settings.rs b/crates/defguard_core/src/handlers/settings.rs index 5ae0ccc8a..9bfb1caf2 100644 --- a/crates/defguard_core/src/handlers/settings.rs +++ b/crates/defguard_core/src/handlers/settings.rs @@ -61,7 +61,7 @@ pub async fn update_settings( info!("User {} updated settings", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::SettingsUpdated, + event: Box::new(ApiEventType::SettingsUpdated), })?; Ok(ApiResponse::default()) @@ -109,7 +109,7 @@ pub async fn set_default_branding( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::SettingsDefaultBrandingRestored, + event: Box::new(ApiEventType::SettingsDefaultBrandingRestored), })?; Ok(ApiResponse { json: json!(settings), @@ -164,7 +164,7 @@ pub async fn patch_settings( info!("Admin {} patched settings.", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::SettingsUpdatedPartial, + event: Box::new(ApiEventType::SettingsUpdatedPartial), })?; Ok(ApiResponse::default()) } diff --git a/crates/defguard_core/src/handlers/ssh_authorized_keys.rs b/crates/defguard_core/src/handlers/ssh_authorized_keys.rs index cfbe17717..7916dd667 100644 --- a/crates/defguard_core/src/handlers/ssh_authorized_keys.rs +++ b/crates/defguard_core/src/handlers/ssh_authorized_keys.rs @@ -213,7 +213,7 @@ pub async fn add_authentication_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuthenticationKeyAdded { key }, + event: Box::new(ApiEventType::AuthenticationKeyAdded { key }), })?; Ok(ApiResponse { @@ -255,7 +255,7 @@ pub async fn delete_authentication_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuthenticationKeyRemoved { key }, + event: Box::new(ApiEventType::AuthenticationKeyRemoved { key }), })?; } else { error!("Key with id {} not found", key_id); @@ -305,11 +305,11 @@ pub async fn rename_authentication_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::AuthenticationKeyRenamed { + event: Box::new(ApiEventType::AuthenticationKeyRenamed { old_name, new_name: key.name.clone(), key, - }, + }), })?; } else { error!( diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index a14164153..7fc510f56 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -342,7 +342,7 @@ pub async fn add_user( } appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserAdded { user }, + event: Box::new(ApiEventType::UserAdded { user }), })?; Ok(ApiResponse { json: json!(&user_info), @@ -444,7 +444,7 @@ pub async fn start_enrollment( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::EnrollmentTokenAdded { user }, + event: Box::new(ApiEventType::EnrollmentTokenAdded { user }), })?; Ok(ApiResponse { @@ -545,7 +545,7 @@ pub async fn start_remote_desktop_configuration( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::ClientConfigurationTokenAdded { user }, + event: Box::new(ApiEventType::ClientConfigurationTokenAdded { user }), })?; Ok(ApiResponse { @@ -747,10 +747,10 @@ pub async fn modify_user( info!("User {} updated user {username}", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserModified { + event: Box::new(ApiEventType::UserModified { before, after: user, - }, + }), })?; Ok(ApiResponse::default()) } @@ -821,7 +821,7 @@ pub async fn delete_user( info!("User {} deleted user {}", session.user.username, &username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserRemoved { user }, + event: Box::new(ApiEventType::UserRemoved { user }), })?; Ok(ApiResponse::default()) } else { @@ -884,7 +884,7 @@ pub async fn change_self_password( info!("User {} changed his password.", &user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::PasswordChanged, + event: Box::new(ApiEventType::PasswordChanged), })?; Ok(ApiResponse { @@ -969,7 +969,7 @@ pub async fn change_password( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::PasswordChangedByAdmin { user }, + event: Box::new(ApiEventType::PasswordChangedByAdmin { user }), })?; Ok(ApiResponse::default()) } else { @@ -1083,7 +1083,7 @@ pub async fn reset_password( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::PasswordReset { user }, + event: Box::new(ApiEventType::PasswordReset { user }), })?; Ok(ApiResponse::default()) } else { @@ -1141,7 +1141,7 @@ pub async fn delete_security_key( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::MfaSecurityKeyRemoved { key: webauthn }, + event: Box::new(ApiEventType::MfaSecurityKeyRemoved { key: webauthn }), })?; Ok(ApiResponse::default()) } else { diff --git a/crates/defguard_core/src/handlers/webhooks.rs b/crates/defguard_core/src/handlers/webhooks.rs index 34dc4b312..d30374f39 100644 --- a/crates/defguard_core/src/handlers/webhooks.rs +++ b/crates/defguard_core/src/handlers/webhooks.rs @@ -27,7 +27,7 @@ pub async fn add_webhook( info!("User {} added webhook {url}", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::WebHookAdded { webhook }, + event: Box::new(ApiEventType::WebHookAdded { webhook }), })?; StatusCode::CREATED } @@ -92,10 +92,10 @@ pub async fn change_webhook( info!("User {} updated webhook {id}", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::WebHookModified { + event: Box::new(ApiEventType::WebHookModified { before, after: webhook, - }, + }), })?; StatusCode::OK } @@ -122,7 +122,7 @@ pub async fn delete_webhook( info!("User {} deleted webhook {id}", session.user.username); appstate.emit_event(ApiEvent { context, - event: ApiEventType::WebHookRemoved { webhook }, + event: Box::new(ApiEventType::WebHookRemoved { webhook }), })?; StatusCode::OK } @@ -161,10 +161,10 @@ pub async fn change_enabled( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::WebHookStateChanged { + event: Box::new(ApiEventType::WebHookStateChanged { enabled: webhook.enabled, webhook, - }, + }), })?; StatusCode::OK } diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index 419a88916..95876b4cf 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -174,9 +174,9 @@ pub(crate) async fn create_network( appstate.emit_event(ApiEvent { context, - event: ApiEventType::VpnLocationAdded { + event: Box::new(ApiEventType::VpnLocationAdded { location: network.clone(), - }, + }), })?; update_counts(&appstate.pool).await?; @@ -263,10 +263,10 @@ pub(crate) async fn modify_network( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::VpnLocationModified { + event: Box::new(ApiEventType::VpnLocationModified { before, after: network.clone(), - }, + }), })?; Ok(ApiResponse { json: json!(network), @@ -318,7 +318,7 @@ pub(crate) async fn delete_network( ); appstate.emit_event(ApiEvent { context, - event: ApiEventType::VpnLocationRemoved { location: network }, + event: Box::new(ApiEventType::VpnLocationRemoved { location: network }), })?; update_counts(&appstate.pool).await?; @@ -533,9 +533,9 @@ pub(crate) async fn import_network( info!("Imported network {network} with {} devices", devices.len()); appstate.emit_event(ApiEvent { context, - event: ApiEventType::VpnLocationAdded { + event: Box::new(ApiEventType::VpnLocationAdded { location: network.clone(), - }, + }), })?; update_counts(&appstate.pool).await?; @@ -801,10 +801,10 @@ pub(crate) async fn add_device( appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserDeviceAdded { + event: Box::new(ApiEventType::UserDeviceAdded { device, owner: user, - }, + }), })?; Ok(ApiResponse { @@ -914,11 +914,11 @@ pub(crate) async fn modify_device( let owner = device.get_owner(&appstate.pool).await?; appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserDeviceModified { + event: Box::new(ApiEventType::UserDeviceModified { owner, before, after: device.clone(), - }, + }), })?; Ok(ApiResponse { @@ -1047,7 +1047,7 @@ pub(crate) async fn delete_device( let owner = device_info.device.get_owner(&mut *transaction).await?; appstate.emit_event(ApiEvent { context, - event: ApiEventType::UserDeviceRemoved { device, owner }, + event: Box::new(ApiEventType::UserDeviceRemoved { device, owner }), })? } DeviceType::Network => { @@ -1058,7 +1058,7 @@ pub(crate) async fn delete_device( if let Some(location) = location { appstate.emit_event(ApiEvent { context, - event: ApiEventType::NetworkDeviceRemoved { device, location }, + event: Box::new(ApiEventType::NetworkDeviceRemoved { device, location }), })?; } else { error!( diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 084e349c4..255ad6d46 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -74,7 +74,7 @@ pub async fn run_event_logger( LoggerEvent::Defguard(event) => { let module = AuditModule::Defguard; - let (event_type, metadata) = match event { + let (event_type, metadata) = match *event { DefguardEvent::UserLogin => (EventType::UserLogin, None), DefguardEvent::UserLoginFailed => (EventType::UserLoginFailed, None), DefguardEvent::UserMfaLogin { mfa_method } => ( @@ -395,7 +395,7 @@ pub async fn run_event_logger( } LoggerEvent::Vpn(event) => { let module = AuditModule::Vpn; - let (event_type, metadata) = match event { + let (event_type, metadata) = match *event { VpnEvent::MfaFailed { location, device, @@ -439,7 +439,7 @@ pub async fn run_event_logger( } LoggerEvent::Enrollment(event) => { let module = AuditModule::Enrollment; - let (event_type, metadata) = match event { + let (event_type, metadata) = match *event { EnrollmentEvent::EnrollmentStarted => { (EventType::EnrollmentStarted, None) } diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index b33a52512..c8febaacf 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -25,13 +25,11 @@ impl EventLoggerMessage { } /// Possible audit event types split by module -// TODO: remove lint override below once all events are updated to pass whole objects -#[allow(clippy::large_enum_variant)] pub enum LoggerEvent { - Defguard(DefguardEvent), - Client(ClientEvent), - Vpn(VpnEvent), - Enrollment(EnrollmentEvent), + Defguard(Box), + Client(Box), + Vpn(Box), + Enrollment(Box), } /// Shared context that's included in all events diff --git a/crates/defguard_event_router/src/events.rs b/crates/defguard_event_router/src/events.rs index 0ee2fb18f..eb6d5f7d8 100644 --- a/crates/defguard_event_router/src/events.rs +++ b/crates/defguard_event_router/src/events.rs @@ -5,7 +5,6 @@ use defguard_core::events::{ApiEvent, BidiStreamEvent, GrpcEvent, InternalEvent} /// System components can send events to the event router through their own event channels. /// The enum itself is organized based on event source to make splitting logic into smaller chunks easier. // TODO: remove lint override below once all events are updated to pass whole objects -#[allow(clippy::large_enum_variant)] pub enum Event { Api(ApiEvent), Grpc(GrpcEvent), diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 32c68d19b..984c2fa36 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -7,194 +7,194 @@ use crate::{error::EventRouterError, EventRouter}; impl EventRouter { pub(crate) fn handle_api_event(&self, event: ApiEvent) -> Result<(), EventRouterError> { debug!("Processing API event"); - let logger_event = match event.event { - ApiEventType::UserLogin => LoggerEvent::Defguard(DefguardEvent::UserLogin), - ApiEventType::UserLoginFailed => LoggerEvent::Defguard(DefguardEvent::UserLoginFailed), + let logger_event = match *event.event { + ApiEventType::UserLogin => LoggerEvent::Defguard(Box::new(DefguardEvent::UserLogin)), + ApiEventType::UserLoginFailed => LoggerEvent::Defguard(Box::new(DefguardEvent::UserLoginFailed)), ApiEventType::UserMfaLogin { mfa_method } => { - LoggerEvent::Defguard(DefguardEvent::UserMfaLogin { mfa_method }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserMfaLogin { mfa_method })) } ApiEventType::UserMfaLoginFailed { mfa_method } => { - LoggerEvent::Defguard(DefguardEvent::UserMfaLoginFailed { mfa_method }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserMfaLoginFailed { mfa_method })) } ApiEventType::RecoveryCodeUsed => { - LoggerEvent::Defguard(DefguardEvent::RecoveryCodeUsed) + LoggerEvent::Defguard(Box::new(DefguardEvent::RecoveryCodeUsed)) } - ApiEventType::UserLogout => LoggerEvent::Defguard(DefguardEvent::UserLogout), + ApiEventType::UserLogout => LoggerEvent::Defguard(Box::new(DefguardEvent::UserLogout)), ApiEventType::UserAdded { user } => { - LoggerEvent::Defguard(DefguardEvent::UserAdded { user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserAdded { user })) } ApiEventType::UserRemoved { user } => { - LoggerEvent::Defguard(DefguardEvent::UserRemoved { user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserRemoved { user })) } ApiEventType::UserModified { before, after } => { - LoggerEvent::Defguard(DefguardEvent::UserModified { before, after }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserModified { before, after })) } - ApiEventType::MfaDisabled => LoggerEvent::Defguard(DefguardEvent::MfaDisabled), - ApiEventType::MfaTotpDisabled => LoggerEvent::Defguard(DefguardEvent::MfaTotpDisabled), - ApiEventType::MfaTotpEnabled => LoggerEvent::Defguard(DefguardEvent::MfaTotpEnabled), + ApiEventType::MfaDisabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaDisabled)), + ApiEventType::MfaTotpDisabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaTotpDisabled)), + ApiEventType::MfaTotpEnabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaTotpEnabled)), ApiEventType::MfaEmailDisabled => { - LoggerEvent::Defguard(DefguardEvent::MfaEmailDisabled) + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaEmailDisabled)) } - ApiEventType::MfaEmailEnabled => LoggerEvent::Defguard(DefguardEvent::MfaEmailEnabled), + ApiEventType::MfaEmailEnabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaEmailEnabled)), ApiEventType::MfaSecurityKeyAdded { key } => { - LoggerEvent::Defguard(DefguardEvent::MfaSecurityKeyAdded { key }) + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaSecurityKeyAdded { key })) } ApiEventType::MfaSecurityKeyRemoved { key } => { - LoggerEvent::Defguard(DefguardEvent::MfaSecurityKeyRemoved { key }) + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaSecurityKeyRemoved { key })) } ApiEventType::UserDeviceAdded { owner, device } => { - LoggerEvent::Defguard(DefguardEvent::UserDeviceAdded { device, owner }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserDeviceAdded { device, owner })) } ApiEventType::UserDeviceRemoved { owner, device } => { - LoggerEvent::Defguard(DefguardEvent::UserDeviceRemoved { device, owner }) + LoggerEvent::Defguard(Box::new(DefguardEvent::UserDeviceRemoved { device, owner })) } ApiEventType::UserDeviceModified { owner, before, after, - } => LoggerEvent::Defguard(DefguardEvent::UserDeviceModified { + } => LoggerEvent::Defguard(Box::new(DefguardEvent::UserDeviceModified { owner, before, after, - }), + })), ApiEventType::NetworkDeviceAdded { device, location } => { - LoggerEvent::Defguard(DefguardEvent::NetworkDeviceAdded { device, location }) + LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceAdded { device, location })) } ApiEventType::NetworkDeviceModified { before, after, location, - } => LoggerEvent::Defguard(DefguardEvent::NetworkDeviceModified { + } => LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceModified { before, after, location, - }), + })), ApiEventType::NetworkDeviceRemoved { device, location } => { - LoggerEvent::Defguard(DefguardEvent::NetworkDeviceRemoved { device, location }) + LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceRemoved { device, location })) } ApiEventType::AuditStreamCreated { stream } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(DefguardEvent::AuditStreamCreated { stream }) + LoggerEvent::Defguard(Box::new(DefguardEvent::AuditStreamCreated { stream })) } ApiEventType::AuditStreamModified { before, after } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(DefguardEvent::AuditStreamModified { before, after }) + LoggerEvent::Defguard(Box::new(DefguardEvent::AuditStreamModified { before, after })) } ApiEventType::AuditStreamRemoved { stream } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(DefguardEvent::AuditStreamRemoved { stream }) + LoggerEvent::Defguard(Box::new(DefguardEvent::AuditStreamRemoved { stream })) } ApiEventType::VpnLocationAdded { location } => { - LoggerEvent::Defguard(DefguardEvent::VpnLocationAdded { location }) + LoggerEvent::Defguard(Box::new(DefguardEvent::VpnLocationAdded { location })) } ApiEventType::VpnLocationRemoved { location } => { - LoggerEvent::Defguard(DefguardEvent::VpnLocationRemoved { location }) + LoggerEvent::Defguard(Box::new(DefguardEvent::VpnLocationRemoved { location })) } ApiEventType::VpnLocationModified { before, after } => { - LoggerEvent::Defguard(DefguardEvent::VpnLocationModified { before, after }) + LoggerEvent::Defguard(Box::new(DefguardEvent::VpnLocationModified { before, after })) } ApiEventType::ApiTokenAdded { owner, token } => { - LoggerEvent::Defguard(DefguardEvent::ApiTokenAdded { owner, token }) + LoggerEvent::Defguard(Box::new(DefguardEvent::ApiTokenAdded { owner, token })) } ApiEventType::ApiTokenRemoved { owner, token } => { - LoggerEvent::Defguard(DefguardEvent::ApiTokenRemoved { owner, token }) + LoggerEvent::Defguard(Box::new(DefguardEvent::ApiTokenRemoved { owner, token })) } ApiEventType::ApiTokenRenamed { owner, token, old_name, new_name, - } => LoggerEvent::Defguard(DefguardEvent::ApiTokenRenamed { + } => LoggerEvent::Defguard(Box::new(DefguardEvent::ApiTokenRenamed { owner, token, old_name, new_name, - }), + })), ApiEventType::OpenIdAppAdded { app } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppAdded { app }) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppAdded { app })) } ApiEventType::OpenIdAppRemoved { app } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppRemoved { app }) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppRemoved { app })) } ApiEventType::OpenIdAppModified { before, after } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppModified { before, after }) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppModified { before, after })) } ApiEventType::OpenIdAppStateChanged { app, enabled } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdAppStateChanged { app, enabled }) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppStateChanged { app, enabled })) } ApiEventType::OpenIdProviderRemoved { provider } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdProviderRemoved { provider }) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdProviderRemoved { provider })) } ApiEventType::OpenIdProviderModified { provider } => { - LoggerEvent::Defguard(DefguardEvent::OpenIdProviderModified { provider }) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdProviderModified { provider })) } - ApiEventType::SettingsUpdated => LoggerEvent::Defguard(DefguardEvent::SettingsUpdated), + ApiEventType::SettingsUpdated => LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsUpdated)), ApiEventType::SettingsUpdatedPartial => { - LoggerEvent::Defguard(DefguardEvent::SettingsUpdatedPartial) + LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsUpdatedPartial)) } ApiEventType::SettingsDefaultBrandingRestored => { - LoggerEvent::Defguard(DefguardEvent::SettingsDefaultBrandingRestored) + LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsDefaultBrandingRestored)) } ApiEventType::GroupsBulkAssigned { users, groups } => { - LoggerEvent::Defguard(DefguardEvent::GroupsBulkAssigned { users, groups }) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupsBulkAssigned { users, groups })) } ApiEventType::GroupAdded { group } => { - LoggerEvent::Defguard(DefguardEvent::GroupAdded { group }) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupAdded { group })) } ApiEventType::GroupModified { before, after } => { - LoggerEvent::Defguard(DefguardEvent::GroupModified { before, after }) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupModified { before, after })) } ApiEventType::GroupRemoved { group } => { - LoggerEvent::Defguard(DefguardEvent::GroupRemoved { group }) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupRemoved { group })) } ApiEventType::GroupMemberAdded { group, user } => { - LoggerEvent::Defguard(DefguardEvent::GroupMemberAdded { group, user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupMemberAdded { group, user })) } ApiEventType::GroupMemberRemoved { group, user } => { - LoggerEvent::Defguard(DefguardEvent::GroupMemberRemoved { group, user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupMemberRemoved { group, user })) } ApiEventType::WebHookAdded { webhook } => { - LoggerEvent::Defguard(DefguardEvent::WebHookAdded { webhook }) + LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookAdded { webhook })) } ApiEventType::WebHookModified { before, after } => { - LoggerEvent::Defguard(DefguardEvent::WebHookModified { before, after }) + LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookModified { before, after })) } ApiEventType::WebHookRemoved { webhook } => { - LoggerEvent::Defguard(DefguardEvent::WebHookRemoved { webhook }) + LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookRemoved { webhook })) } ApiEventType::WebHookStateChanged { webhook, enabled } => { - LoggerEvent::Defguard(DefguardEvent::WebHookStateChanged { webhook, enabled }) + LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookStateChanged { webhook, enabled })) } ApiEventType::AuthenticationKeyAdded { key } => { - LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyAdded { key }) + LoggerEvent::Defguard(Box::new(DefguardEvent::AuthenticationKeyAdded { key })) } ApiEventType::AuthenticationKeyRemoved { key } => { - LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRemoved { key }) + LoggerEvent::Defguard(Box::new(DefguardEvent::AuthenticationKeyRemoved { key })) } ApiEventType::AuthenticationKeyRenamed { key, old_name, new_name, - } => LoggerEvent::Defguard(DefguardEvent::AuthenticationKeyRenamed { + } => LoggerEvent::Defguard(Box::new(DefguardEvent::AuthenticationKeyRenamed { key, old_name, new_name, - }), + })), ApiEventType::EnrollmentTokenAdded { user } => { - LoggerEvent::Enrollment(EnrollmentEvent::TokenAdded { user }) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::TokenAdded { user })) } - ApiEventType::PasswordChanged => LoggerEvent::Defguard(DefguardEvent::PasswordChanged), + ApiEventType::PasswordChanged => LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordChanged)), ApiEventType::PasswordChangedByAdmin { user } => { - LoggerEvent::Defguard(DefguardEvent::PasswordChangedByAdmin { user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordChangedByAdmin { user })) } ApiEventType::PasswordReset { user } => { - LoggerEvent::Defguard(DefguardEvent::PasswordReset { user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordReset { user })) } ApiEventType::ClientConfigurationTokenAdded { user } => { - LoggerEvent::Defguard(DefguardEvent::ClientConfigurationTokenAdded { user }) + LoggerEvent::Defguard(Box::new(DefguardEvent::ClientConfigurationTokenAdded { user })) } }; self.log_event(event.context.into(), logger_event) diff --git a/crates/defguard_event_router/src/handlers/bidi.rs b/crates/defguard_event_router/src/handlers/bidi.rs index b6a3681dc..6b847e991 100644 --- a/crates/defguard_event_router/src/handlers/bidi.rs +++ b/crates/defguard_event_router/src/handlers/bidi.rs @@ -11,50 +11,52 @@ impl EventRouter { debug!("Processing bidi gRPC stream event: {event:?}"); let BidiStreamEvent { context, event } = event; - let logger_event = match event { - BidiStreamEventType::Enrollment(event) => match event { + let logger_event = match *event { + BidiStreamEventType::Enrollment(event) => match *event { events::EnrollmentEvent::EnrollmentStarted => { - LoggerEvent::Enrollment(EnrollmentEvent::EnrollmentStarted) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::EnrollmentStarted)) } events::EnrollmentEvent::EnrollmentCompleted => { - LoggerEvent::Enrollment(EnrollmentEvent::EnrollmentCompleted) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::EnrollmentCompleted)) } events::EnrollmentEvent::EnrollmentDeviceAdded { device } => { - LoggerEvent::Enrollment(EnrollmentEvent::EnrollmentDeviceAdded { device }) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::EnrollmentDeviceAdded { + device, + })) } }, - BidiStreamEventType::PasswordReset(event) => match event { + BidiStreamEventType::PasswordReset(event) => match *event { PasswordResetEvent::PasswordResetRequested => { - LoggerEvent::Enrollment(EnrollmentEvent::PasswordResetRequested) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::PasswordResetRequested)) } PasswordResetEvent::PasswordResetStarted => { - LoggerEvent::Enrollment(EnrollmentEvent::PasswordResetStarted) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::PasswordResetStarted)) } PasswordResetEvent::PasswordResetCompleted => { - LoggerEvent::Enrollment(EnrollmentEvent::PasswordResetCompleted) + LoggerEvent::Enrollment(Box::new(EnrollmentEvent::PasswordResetCompleted)) } }, - BidiStreamEventType::DesktopClientMfa(event) => match event { + BidiStreamEventType::DesktopClientMfa(event) => match *event { DesktopClientMfaEvent::Connected { location, device, method, - } => LoggerEvent::Vpn(VpnEvent::ConnectedToMfaLocation { + } => LoggerEvent::Vpn(Box::new(VpnEvent::ConnectedToMfaLocation { location, device, method, - }), + })), DesktopClientMfaEvent::Failed { location, device, method, - } => LoggerEvent::Vpn(VpnEvent::MfaFailed { + } => LoggerEvent::Vpn(Box::new(VpnEvent::MfaFailed { location, device, method, - }), + })), }, }; diff --git a/crates/defguard_event_router/src/handlers/grpc.rs b/crates/defguard_event_router/src/handlers/grpc.rs index 282de5c99..2f4b62f93 100644 --- a/crates/defguard_event_router/src/handlers/grpc.rs +++ b/crates/defguard_event_router/src/handlers/grpc.rs @@ -18,7 +18,7 @@ impl EventRouter { } => { self.log_event( context.into(), - LoggerEvent::Vpn(VpnEvent::ConnectedToLocation { location, device }), + LoggerEvent::Vpn(Box::new(VpnEvent::ConnectedToLocation { location, device })), )?; } GrpcEvent::ClientDisconnected { @@ -28,7 +28,10 @@ impl EventRouter { } => { self.log_event( context.into(), - LoggerEvent::Vpn(VpnEvent::DisconnectedFromLocation { location, device }), + LoggerEvent::Vpn(Box::new(VpnEvent::DisconnectedFromLocation { + location, + device, + })), )?; } } diff --git a/crates/defguard_event_router/src/handlers/internal.rs b/crates/defguard_event_router/src/handlers/internal.rs index d07cfb4ba..2dee7b195 100644 --- a/crates/defguard_event_router/src/handlers/internal.rs +++ b/crates/defguard_event_router/src/handlers/internal.rs @@ -16,7 +16,10 @@ impl EventRouter { let device = context.device.clone(); self.log_event( context.into(), - LoggerEvent::Vpn(VpnEvent::DisconnectedFromMfaLocation { device, location }), + LoggerEvent::Vpn(Box::new(VpnEvent::DisconnectedFromMfaLocation { + device, + location, + })), ) } } From 67fbbdf788347d78f84b6497368dbca3021a8035 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 14:32:48 +0200 Subject: [PATCH 20/23] Allow large enum variant on GatewayServerError --- crates/defguard_core/src/grpc/gateway/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/defguard_core/src/grpc/gateway/mod.rs b/crates/defguard_core/src/grpc/gateway/mod.rs index ce8e80c16..ee7d5c7cf 100644 --- a/crates/defguard_core/src/grpc/gateway/mod.rs +++ b/crates/defguard_core/src/grpc/gateway/mod.rs @@ -54,6 +54,7 @@ pub fn send_multiple_wireguard_events(events: Vec, wg_tx: &Sender< } } +#[allow(clippy::large_enum_variant)] #[derive(Debug, Error)] pub enum GatewayServerError { #[error("Failed to acquire lock on VPN client state map")] From 9dd03baace140709d1f42bf5da0e77057c9c41a8 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 14:41:23 +0200 Subject: [PATCH 21/23] Don't box whole events --- crates/defguard_core/src/events.rs | 2 +- .../src/grpc/desktop_client_mfa.rs | 12 ++-- crates/defguard_core/src/grpc/enrollment.rs | 2 +- .../defguard_core/src/grpc/password_reset.rs | 2 +- .../defguard_event_router/src/handlers/api.rs | 67 ++++++++++++++----- .../src/handlers/bidi.rs | 2 +- 6 files changed, 62 insertions(+), 25 deletions(-) diff --git a/crates/defguard_core/src/events.rs b/crates/defguard_core/src/events.rs index 0b4719663..8d557f7f0 100644 --- a/crates/defguard_core/src/events.rs +++ b/crates/defguard_core/src/events.rs @@ -300,7 +300,7 @@ impl BidiRequestContext { #[derive(Debug)] pub struct BidiStreamEvent { pub context: BidiRequestContext, - pub event: Box, + pub event: BidiStreamEventType, } /// Wrapper enum for different types of events emitted by the bidi stream. diff --git a/crates/defguard_core/src/grpc/desktop_client_mfa.rs b/crates/defguard_core/src/grpc/desktop_client_mfa.rs index 0d545e8bc..f4d228903 100644 --- a/crates/defguard_core/src/grpc/desktop_client_mfa.rs +++ b/crates/defguard_core/src/grpc/desktop_client_mfa.rs @@ -250,13 +250,13 @@ impl ClientMfaServer { error!("Provided TOTP code is not valid"); self.emit_event(BidiStreamEvent { context, - event: Box::new(BidiStreamEventType::DesktopClientMfa(Box::new( + event: BidiStreamEventType::DesktopClientMfa(Box::new( DesktopClientMfaEvent::Failed { location: location.clone(), device: device.clone(), method: (*method).into(), }, - ))), + )), })?; return Err(Status::unauthenticated("unauthorized")); } @@ -266,13 +266,13 @@ impl ClientMfaServer { error!("Provided email code is not valid"); self.emit_event(BidiStreamEvent { context, - event: Box::new(BidiStreamEventType::DesktopClientMfa(Box::new( + event: BidiStreamEventType::DesktopClientMfa(Box::new( DesktopClientMfaEvent::Failed { location: location.clone(), device: device.clone(), method: (*method).into(), }, - ))), + )), })?; return Err(Status::unauthenticated("unauthorized")); } @@ -333,13 +333,13 @@ impl ClientMfaServer { ); self.emit_event(BidiStreamEvent { context, - event: Box::new(BidiStreamEventType::DesktopClientMfa(Box::new( + event: BidiStreamEventType::DesktopClientMfa(Box::new( DesktopClientMfaEvent::Connected { location: location.clone(), device: device.clone(), method: (*method).into(), }, - ))), + )), })?; // remove login session from map diff --git a/crates/defguard_core/src/grpc/enrollment.rs b/crates/defguard_core/src/grpc/enrollment.rs index 03d9a49ee..4b0205e14 100644 --- a/crates/defguard_core/src/grpc/enrollment.rs +++ b/crates/defguard_core/src/grpc/enrollment.rs @@ -104,7 +104,7 @@ impl EnrollmentServer { ) -> Result<(), SendError> { let event = BidiStreamEvent { context, - event: Box::new(BidiStreamEventType::Enrollment(Box::new(event))), + event: BidiStreamEventType::Enrollment(Box::new(event)), }; self.bidi_event_tx.send(event) diff --git a/crates/defguard_core/src/grpc/password_reset.rs b/crates/defguard_core/src/grpc/password_reset.rs index 6799c91fc..b84792782 100644 --- a/crates/defguard_core/src/grpc/password_reset.rs +++ b/crates/defguard_core/src/grpc/password_reset.rs @@ -83,7 +83,7 @@ impl PasswordResetServer { ) -> Result<(), SendError> { let event = BidiStreamEvent { context, - event: Box::new(BidiStreamEventType::PasswordReset(Box::new(event))), + event: BidiStreamEventType::PasswordReset(Box::new(event)), }; self.bidi_event_tx.send(event) diff --git a/crates/defguard_event_router/src/handlers/api.rs b/crates/defguard_event_router/src/handlers/api.rs index 984c2fa36..2d3ef2637 100644 --- a/crates/defguard_event_router/src/handlers/api.rs +++ b/crates/defguard_event_router/src/handlers/api.rs @@ -9,7 +9,9 @@ impl EventRouter { debug!("Processing API event"); let logger_event = match *event.event { ApiEventType::UserLogin => LoggerEvent::Defguard(Box::new(DefguardEvent::UserLogin)), - ApiEventType::UserLoginFailed => LoggerEvent::Defguard(Box::new(DefguardEvent::UserLoginFailed)), + ApiEventType::UserLoginFailed => { + LoggerEvent::Defguard(Box::new(DefguardEvent::UserLoginFailed)) + } ApiEventType::UserMfaLogin { mfa_method } => { LoggerEvent::Defguard(Box::new(DefguardEvent::UserMfaLogin { mfa_method })) } @@ -29,13 +31,21 @@ impl EventRouter { ApiEventType::UserModified { before, after } => { LoggerEvent::Defguard(Box::new(DefguardEvent::UserModified { before, after })) } - ApiEventType::MfaDisabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaDisabled)), - ApiEventType::MfaTotpDisabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaTotpDisabled)), - ApiEventType::MfaTotpEnabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaTotpEnabled)), + ApiEventType::MfaDisabled => { + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaDisabled)) + } + ApiEventType::MfaTotpDisabled => { + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaTotpDisabled)) + } + ApiEventType::MfaTotpEnabled => { + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaTotpEnabled)) + } ApiEventType::MfaEmailDisabled => { LoggerEvent::Defguard(Box::new(DefguardEvent::MfaEmailDisabled)) } - ApiEventType::MfaEmailEnabled => LoggerEvent::Defguard(Box::new(DefguardEvent::MfaEmailEnabled)), + ApiEventType::MfaEmailEnabled => { + LoggerEvent::Defguard(Box::new(DefguardEvent::MfaEmailEnabled)) + } ApiEventType::MfaSecurityKeyAdded { key } => { LoggerEvent::Defguard(Box::new(DefguardEvent::MfaSecurityKeyAdded { key })) } @@ -58,7 +68,10 @@ impl EventRouter { after, })), ApiEventType::NetworkDeviceAdded { device, location } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceAdded { device, location })) + LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceAdded { + device, + location, + })) } ApiEventType::NetworkDeviceModified { before, @@ -70,7 +83,10 @@ impl EventRouter { location, })), ApiEventType::NetworkDeviceRemoved { device, location } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceRemoved { device, location })) + LoggerEvent::Defguard(Box::new(DefguardEvent::NetworkDeviceRemoved { + device, + location, + })) } ApiEventType::AuditStreamCreated { stream } => { // Notify stream manager about configuration changes @@ -80,7 +96,10 @@ impl EventRouter { ApiEventType::AuditStreamModified { before, after } => { // Notify stream manager about configuration changes self.audit_stream_reload_notify.notify_waiters(); - LoggerEvent::Defguard(Box::new(DefguardEvent::AuditStreamModified { before, after })) + LoggerEvent::Defguard(Box::new(DefguardEvent::AuditStreamModified { + before, + after, + })) } ApiEventType::AuditStreamRemoved { stream } => { // Notify stream manager about configuration changes @@ -94,7 +113,10 @@ impl EventRouter { LoggerEvent::Defguard(Box::new(DefguardEvent::VpnLocationRemoved { location })) } ApiEventType::VpnLocationModified { before, after } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::VpnLocationModified { before, after })) + LoggerEvent::Defguard(Box::new(DefguardEvent::VpnLocationModified { + before, + after, + })) } ApiEventType::ApiTokenAdded { owner, token } => { LoggerEvent::Defguard(Box::new(DefguardEvent::ApiTokenAdded { owner, token })) @@ -123,7 +145,10 @@ impl EventRouter { LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppModified { before, after })) } ApiEventType::OpenIdAppStateChanged { app, enabled } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppStateChanged { app, enabled })) + LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdAppStateChanged { + app, + enabled, + })) } ApiEventType::OpenIdProviderRemoved { provider } => { LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdProviderRemoved { provider })) @@ -131,7 +156,9 @@ impl EventRouter { ApiEventType::OpenIdProviderModified { provider } => { LoggerEvent::Defguard(Box::new(DefguardEvent::OpenIdProviderModified { provider })) } - ApiEventType::SettingsUpdated => LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsUpdated)), + ApiEventType::SettingsUpdated => { + LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsUpdated)) + } ApiEventType::SettingsUpdatedPartial => { LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsUpdatedPartial)) } @@ -139,7 +166,10 @@ impl EventRouter { LoggerEvent::Defguard(Box::new(DefguardEvent::SettingsDefaultBrandingRestored)) } ApiEventType::GroupsBulkAssigned { users, groups } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::GroupsBulkAssigned { users, groups })) + LoggerEvent::Defguard(Box::new(DefguardEvent::GroupsBulkAssigned { + users, + groups, + })) } ApiEventType::GroupAdded { group } => { LoggerEvent::Defguard(Box::new(DefguardEvent::GroupAdded { group })) @@ -166,7 +196,10 @@ impl EventRouter { LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookRemoved { webhook })) } ApiEventType::WebHookStateChanged { webhook, enabled } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookStateChanged { webhook, enabled })) + LoggerEvent::Defguard(Box::new(DefguardEvent::WebHookStateChanged { + webhook, + enabled, + })) } ApiEventType::AuthenticationKeyAdded { key } => { LoggerEvent::Defguard(Box::new(DefguardEvent::AuthenticationKeyAdded { key })) @@ -186,7 +219,9 @@ impl EventRouter { ApiEventType::EnrollmentTokenAdded { user } => { LoggerEvent::Enrollment(Box::new(EnrollmentEvent::TokenAdded { user })) } - ApiEventType::PasswordChanged => LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordChanged)), + ApiEventType::PasswordChanged => { + LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordChanged)) + } ApiEventType::PasswordChangedByAdmin { user } => { LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordChangedByAdmin { user })) } @@ -194,7 +229,9 @@ impl EventRouter { LoggerEvent::Defguard(Box::new(DefguardEvent::PasswordReset { user })) } ApiEventType::ClientConfigurationTokenAdded { user } => { - LoggerEvent::Defguard(Box::new(DefguardEvent::ClientConfigurationTokenAdded { user })) + LoggerEvent::Defguard(Box::new(DefguardEvent::ClientConfigurationTokenAdded { + user, + })) } }; self.log_event(event.context.into(), logger_event) diff --git a/crates/defguard_event_router/src/handlers/bidi.rs b/crates/defguard_event_router/src/handlers/bidi.rs index 6b847e991..5c00c1895 100644 --- a/crates/defguard_event_router/src/handlers/bidi.rs +++ b/crates/defguard_event_router/src/handlers/bidi.rs @@ -11,7 +11,7 @@ impl EventRouter { debug!("Processing bidi gRPC stream event: {event:?}"); let BidiStreamEvent { context, event } = event; - let logger_event = match *event { + let logger_event = match event { BidiStreamEventType::Enrollment(event) => match *event { events::EnrollmentEvent::EnrollmentStarted => { LoggerEvent::Enrollment(Box::new(EnrollmentEvent::EnrollmentStarted)) From c7c442575a1d22ed89f7ae2d0e8d3e26e1395507 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 18 Jun 2025 14:44:07 +0200 Subject: [PATCH 22/23] Remove todo comment --- crates/defguard_event_router/src/events.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/defguard_event_router/src/events.rs b/crates/defguard_event_router/src/events.rs index eb6d5f7d8..ca02c1a57 100644 --- a/crates/defguard_event_router/src/events.rs +++ b/crates/defguard_event_router/src/events.rs @@ -4,7 +4,6 @@ use defguard_core::events::{ApiEvent, BidiStreamEvent, GrpcEvent, InternalEvent} /// /// System components can send events to the event router through their own event channels. /// The enum itself is organized based on event source to make splitting logic into smaller chunks easier. -// TODO: remove lint override below once all events are updated to pass whole objects pub enum Event { Api(ApiEvent), Grpc(GrpcEvent), From 99fb9e29c105e3ab8315f1747169236d07ec2d62 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 23 Jun 2025 09:55:37 +0200 Subject: [PATCH 23/23] Remove client events --- crates/defguard_event_logger/src/lib.rs | 4 ---- crates/defguard_event_logger/src/message.rs | 7 ------- 2 files changed, 11 deletions(-) diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 255ad6d46..500e975c0 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -389,10 +389,6 @@ pub async fn run_event_logger( }; (module, event_type, metadata) } - LoggerEvent::Client(_event) => { - let _module = AuditModule::Client; - unimplemented!() - } LoggerEvent::Vpn(event) => { let module = AuditModule::Vpn; let (event_type, metadata) = match *event { diff --git a/crates/defguard_event_logger/src/message.rs b/crates/defguard_event_logger/src/message.rs index c8febaacf..476e5c0ae 100644 --- a/crates/defguard_event_logger/src/message.rs +++ b/crates/defguard_event_logger/src/message.rs @@ -27,7 +27,6 @@ impl EventLoggerMessage { /// Possible audit event types split by module pub enum LoggerEvent { Defguard(Box), - Client(Box), Vpn(Box), Enrollment(Box), } @@ -267,12 +266,6 @@ pub enum DefguardEvent { }, } -/// Represents audit events related to client applications -pub enum ClientEvent { - DesktopClientActivated { device_id: Id, device_name: String }, - DesktopClientUpdated { device_id: Id, device_name: String }, -} - /// Represents audit events related to VPN pub enum VpnEvent { ConnectedToMfaLocation {