From 168f832aec6239d7f575f453fb9d4abf791e8b6f Mon Sep 17 00:00:00 2001 From: h7x4 Date: Sun, 18 Aug 2024 00:17:55 +0200 Subject: [PATCH] Readd "databases where user has privileges" to `show-user` --- src/cli/user_command.rs | 4 +-- src/server/sql/user_operations.rs | 58 ++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/cli/user_command.rs b/src/cli/user_command.rs index 2b841e8..2a5eaf3 100644 --- a/src/cli/user_command.rs +++ b/src/cli/user_command.rs @@ -290,14 +290,14 @@ async fn show_users( "User", "Password is set", "Locked", - // "Databases where user has privileges" + "Databases where user has privileges" ]); for user in users { table.add_row(row![ user.user, user.has_password, user.is_locked, - // user.databases.join("\n") + user.databases.join("\n") ]); } table.printstd(); diff --git a/src/server/sql/user_operations.rs b/src/server/sql/user_operations.rs index 4c83692..c73333e 100644 --- a/src/server/sql/user_operations.rs +++ b/src/server/sql/user_operations.rs @@ -1,4 +1,6 @@ +use itertools::Itertools; use std::collections::BTreeMap; +use indoc::formatdoc; use serde::{Deserialize, Serialize}; @@ -20,6 +22,8 @@ use crate::{ }, }; +use super::database_privilege_operations::DATABASE_PRIVILEGE_FIELDS; + // NOTE: this function is unsafe because it does no input validation. async fn unsafe_user_exists( db_user: &str, @@ -309,6 +313,9 @@ pub struct DatabaseUser { #[sqlx(rename = "is_locked")] pub is_locked: bool, + + #[sqlx(skip)] + pub databases: Vec, } const DB_USER_SELECT_STATEMENT: &str = r#" @@ -344,13 +351,17 @@ pub async fn list_database_users( continue; } - let result = sqlx::query_as::<_, DatabaseUser>( + let mut result = sqlx::query_as::<_, DatabaseUser>( &(DB_USER_SELECT_STATEMENT.to_string() + "WHERE `mysql`.`user`.`User` = ?"), ) .bind(&db_user) .fetch_optional(&mut *connection) .await; + if let Ok(Some(user)) = result.as_mut() { + append_databases_where_user_has_privileges(user, &mut *connection).await; + } + match result { Ok(Some(user)) => results.insert(db_user, Ok(user)), Ok(None) => results.insert(db_user, Err(ListUsersError::UserDoesNotExist)), @@ -365,11 +376,50 @@ pub async fn list_all_database_users_for_unix_user( unix_user: &UnixUser, connection: &mut MySqlConnection, ) -> ListAllUsersOutput { - sqlx::query_as::<_, DatabaseUser>( + let mut result = sqlx::query_as::<_, DatabaseUser>( &(DB_USER_SELECT_STATEMENT.to_string() + "WHERE `mysql`.`user`.`User` REGEXP ?"), ) .bind(create_user_group_matching_regex(unix_user)) - .fetch_all(connection) + .fetch_all(&mut *connection) .await - .map_err(|err| ListAllUsersError::MySqlError(err.to_string())) + .map_err(|err| ListAllUsersError::MySqlError(err.to_string())); + + if let Ok(users) = result.as_mut() { + for user in users { + append_databases_where_user_has_privileges(user, &mut *connection).await; + } + } + + result +} + +pub async fn append_databases_where_user_has_privileges( + database_user: &mut DatabaseUser, + connection: &mut MySqlConnection, +) { + let database_list = sqlx::query( + formatdoc!( + r#" + SELECT `db` AS `database` + FROM `db` + WHERE `user` = ? AND ({}) + "#, + DATABASE_PRIVILEGE_FIELDS + .iter() + .map(|field| format!("`{}` = 'Y'", field)) + .join(" OR "), + ) + .as_str(), + ) + .bind(database_user.user.clone()) + .fetch_all(&mut *connection) + .await; + + database_user.databases = database_list + .map(|rows| { + rows.into_iter() + .map(|row| row.get::("database")) + .collect() + }) + .unwrap_or_default(); }