show-user: add databases field, report when no users
This commit is contained in:
parent
a3c894061c
commit
25ce0b4ea9
|
@ -15,7 +15,7 @@ nix = { version = "0.28.0", features = ["user"] }
|
||||||
prettytable = "0.10.0"
|
prettytable = "0.10.0"
|
||||||
ratatui = { version = "0.26.2", optional = true }
|
ratatui = { version = "0.26.2", optional = true }
|
||||||
serde = "1.0.198"
|
serde = "1.0.198"
|
||||||
serde_json = "1.0.116"
|
serde_json = { version = "1.0.116", features = ["preserve_order"] }
|
||||||
sqlx = { version = "0.7.4", features = ["runtime-tokio", "mysql", "tls-rustls"] }
|
sqlx = { version = "0.7.4", features = ["runtime-tokio", "mysql", "tls-rustls"] }
|
||||||
tokio = { version = "1.37.0", features = ["rt", "macros"] }
|
tokio = { version = "1.37.0", features = ["rt", "macros"] }
|
||||||
toml = "0.8.12"
|
toml = "0.8.12"
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dialoguer::{Confirm, Password};
|
use dialoguer::{Confirm, Password};
|
||||||
|
use prettytable::Table;
|
||||||
|
use serde_json::json;
|
||||||
use sqlx::{Connection, MySqlConnection};
|
use sqlx::{Connection, MySqlConnection};
|
||||||
|
|
||||||
use crate::core::{common::close_database_connection, user_operations::validate_user_name};
|
use crate::core::{
|
||||||
|
common::close_database_connection,
|
||||||
|
database_operations::get_databases_where_user_has_privileges,
|
||||||
|
user_operations::validate_user_name,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct UserArgs {
|
pub struct UserArgs {
|
||||||
|
@ -196,20 +203,47 @@ async fn show_users(args: UserShowArgs, conn: &mut MySqlConnection) -> anyhow::R
|
||||||
result
|
result
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.json {
|
let mut user_databases: BTreeMap<String, Vec<String>> = BTreeMap::new();
|
||||||
println!("{}", serde_json::to_string_pretty(&users)?);
|
for user in users.iter() {
|
||||||
} else {
|
user_databases.insert(
|
||||||
for user in users {
|
user.user.clone(),
|
||||||
println!(
|
get_databases_where_user_has_privileges(&user.user, conn).await?,
|
||||||
"User '{}': {}",
|
|
||||||
&user.user,
|
|
||||||
if user.has_password {
|
|
||||||
"password set."
|
|
||||||
} else {
|
|
||||||
"no password set."
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if args.json {
|
||||||
|
let users_json = users
|
||||||
|
.into_iter()
|
||||||
|
.map(|user| {
|
||||||
|
json!({
|
||||||
|
"user": user.user,
|
||||||
|
"has_password": user.has_password,
|
||||||
|
"databases": user_databases.get(&user.user).unwrap_or(&vec![]),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Value>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&users_json)
|
||||||
|
.context("Failed to serialize users to JSON")?
|
||||||
|
);
|
||||||
|
} else if users.is_empty() {
|
||||||
|
println!("No users found.");
|
||||||
|
} else {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![
|
||||||
|
"User",
|
||||||
|
"Password is set",
|
||||||
|
"Databases where user has privileges"
|
||||||
|
]);
|
||||||
|
for user in users {
|
||||||
|
table.add_row(row![
|
||||||
|
user.user,
|
||||||
|
user.has_password,
|
||||||
|
user_databases.get(&user.user).unwrap_or(&vec![]).join("\n")
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use indoc::indoc;
|
use indoc::{formatdoc, indoc};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nix::unistd::User;
|
use nix::unistd::User;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -77,6 +77,37 @@ pub async fn get_database_list(conn: &mut MySqlConnection) -> anyhow::Result<Vec
|
||||||
Ok(databases.into_iter().map(|d| d.database).collect())
|
Ok(databases.into_iter().map(|d| d.database).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_databases_where_user_has_privileges(
|
||||||
|
username: &str,
|
||||||
|
conn: &mut MySqlConnection,
|
||||||
|
) -> anyhow::Result<Vec<String>> {
|
||||||
|
let result = 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(username)
|
||||||
|
.fetch_all(conn)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|databases| {
|
||||||
|
databases.try_get::<String, _>("database").unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
pub const DATABASE_PRIVILEGE_FIELDS: [&str; 13] = [
|
pub const DATABASE_PRIVILEGE_FIELDS: [&str; 13] = [
|
||||||
"db",
|
"db",
|
||||||
"user",
|
"user",
|
||||||
|
|
Loading…
Reference in New Issue