diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index 73722c1026..e52eb7f1ac 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -198,7 +198,7 @@ pub struct UserDetails { impl UserDetails { pub async fn from_user(pool: &DbPool, user: &User) -> Result { - let devices = user.devices(pool).await?; + let devices = user.user_devices(pool).await?; let wallets = user.wallets(pool).await?; let security_keys = user.security_keys(pool).await?; diff --git a/src/db/models/user.rs b/src/db/models/user.rs index b9b5948652..4b873e3c96 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -683,24 +683,35 @@ impl User { } } - pub async fn devices(&self, pool: &DbPool) -> Result, SqlxError> { + /// Returns a vector of [`UserDevice`]s (hence the name). + /// [`UserDevice`] is a struct containing additional network info about a device. + /// If you only need [`Device`]s, use [`User::devices()`] instead. + pub async fn user_devices(&self, pool: &DbPool) -> Result, SqlxError> { + let devices = self.devices(pool).await?; + let mut user_devices = Vec::new(); + for device in devices { + if let Some(user_device) = UserDevice::from_device(pool, device).await? { + user_devices.push(user_device); + } + } + Ok(user_devices) + } + + /// Returns a vector of [`Device`]s related to a user. If you want to get [`UserDevice`]s (which contain additional network info), + /// use [`User::user_devices()`] instead. + pub async fn devices<'e, E>(&self, executor: E) -> Result, SqlxError> + where + E: PgExecutor<'e>, + { if let Some(id) = self.id { - let devices = query_as!( + query_as!( Device, "SELECT device.id \"id?\", name, wireguard_pubkey, user_id, created \ FROM device WHERE user_id = $1", id ) - .fetch_all(pool) - .await?; - - let mut user_devices = Vec::new(); - for device in devices { - if let Some(user_device) = UserDevice::from_device(pool, device).await? { - user_devices.push(user_device); - } - } - Ok(user_devices) + .fetch_all(executor) + .await } else { Ok(Vec::new()) } diff --git a/src/grpc/enrollment.rs b/src/grpc/enrollment.rs index 0307b250aa..59e9027506 100644 --- a/src/grpc/enrollment.rs +++ b/src/grpc/enrollment.rs @@ -573,7 +573,7 @@ impl From for AdminInfo { impl InitialUserInfo { async fn from_user(pool: &DbPool, user: User) -> Result { let enrolled = user.is_enrolled(); - let devices = user.devices(pool).await?; + let devices = user.user_devices(pool).await?; let device_names = devices.into_iter().map(|dev| dev.device.name).collect(); Ok(Self { first_name: user.first_name, diff --git a/src/handlers/user.rs b/src/handlers/user.rs index 17b98d70cd..a9411e94f2 100644 --- a/src/handlers/user.rs +++ b/src/handlers/user.rs @@ -15,9 +15,12 @@ use crate::{ appstate::AppState, auth::{SessionInfo, UserAdminRole}, db::{ - models::enrollment::{Token, PASSWORD_RESET_TOKEN_TYPE}, - AppEvent, MFAMethod, OAuth2AuthorizedApp, Settings, User, UserDetails, UserInfo, Wallet, - WebAuthn, WireguardNetwork, + models::{ + device::DeviceInfo, + enrollment::{Token, PASSWORD_RESET_TOKEN_TYPE}, + }, + AppEvent, GatewayEvent, MFAMethod, OAuth2AuthorizedApp, Settings, User, UserDetails, + UserInfo, Wallet, WebAuthn, WireguardNetwork, }, error::WebError, ldap::utils::{ldap_add_user, ldap_change_password, ldap_delete_user, ldap_modify_user}, @@ -710,9 +713,27 @@ pub async fn delete_user( }); } if let Some(user) = User::find_by_username(&appstate.pool, &username).await? { - user.delete(&appstate.pool).await?; - let _result = ldap_delete_user(&appstate.pool, &username).await; + // Get rid of all devices of the deleted user from networks first + debug!( + "User {} deleted user {username}, purging their network devices across all networks.", + session.user.username + ); + let mut transaction = appstate.pool.begin().await?; + let devices = user.devices(&mut *transaction).await?; + let mut events = Vec::new(); + for device in devices { + events.push(GatewayEvent::DeviceDeleted( + DeviceInfo::from_device(&mut *transaction, device).await?, + )); + } + appstate.send_multiple_wireguard_events(events); + debug!("Devices of user {username} purged from networks."); + + user.delete(&mut *transaction).await?; + let _result = ldap_delete_user(&mut *transaction, &username).await; appstate.trigger_action(AppEvent::UserDeleted(username.clone())); + transaction.commit().await?; + info!("User {} deleted user {}", session.user.username, &username); Ok(ApiResponse::default()) } else {