Fix issue where groupless users own all users/dbs

This commit is contained in:
Oystein Kristoffer Tveit 2024-08-04 14:29:34 +02:00 committed by Oystein Kristoffer Tveit
parent 0b1b8c296c
commit eddc0ad5e9
4 changed files with 31 additions and 38 deletions

View File

@ -137,14 +137,14 @@ async fn change_password_for_user(
} }
async fn show_users(args: UserShowArgs, conn: &mut MySqlConnection) -> anyhow::Result<()> { async fn show_users(args: UserShowArgs, conn: &mut MySqlConnection) -> anyhow::Result<()> {
let user = crate::core::common::get_current_unix_user()?; let unix_user = crate::core::common::get_current_unix_user()?;
let users = if args.username.is_empty() { let users = if args.username.is_empty() {
crate::core::user_operations::get_all_database_users_for_user(&user, conn).await? crate::core::user_operations::get_all_database_users_for_unix_user(&unix_user, conn).await?
} else { } else {
let mut result = vec![]; let mut result = vec![];
for username in args.username { for username in args.username {
if let Err(e) = validate_ownership_of_user_name(&username, &user) { if let Err(e) = validate_ownership_of_user_name(&username, &unix_user) {
eprintln!("{}", e); eprintln!("{}", e);
eprintln!("Skipping..."); eprintln!("Skipping...");
continue; continue;

View File

@ -42,6 +42,26 @@ pub fn get_unix_groups(user: &User) -> anyhow::Result<Vec<Group>> {
Ok(groups) Ok(groups)
} }
/// This function creates a regex that matches items (users, databases)
/// that belong to the user or any of the user's groups.
pub fn create_user_group_matching_regex(user: &User) -> String {
let groups = get_unix_groups(user).unwrap_or_default();
if groups.is_empty() {
format!("{}(_.+)?", user.name)
} else {
format!(
"({}|{})(_.+)?",
user.name,
groups
.iter()
.map(|g| g.name.as_str())
.collect::<Vec<_>>()
.join("|")
)
}
}
pub fn validate_prefix_for_user<'a>(name: &'a str, user: &User) -> anyhow::Result<&'a str> { pub fn validate_prefix_for_user<'a>(name: &'a str, user: &User) -> anyhow::Result<&'a str> {
let user_groups = get_unix_groups(user)?; let user_groups = get_unix_groups(user)?;

View File

@ -8,7 +8,8 @@ use serde::{Deserialize, Serialize};
use sqlx::{mysql::MySqlRow, prelude::*, MySqlConnection}; use sqlx::{mysql::MySqlRow, prelude::*, MySqlConnection};
use super::common::{ use super::common::{
get_current_unix_user, get_unix_groups, quote_identifier, validate_prefix_for_user, create_user_group_matching_regex, get_current_unix_user, quote_identifier,
validate_prefix_for_user,
}; };
pub async fn create_database(name: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> { pub async fn create_database(name: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> {
@ -56,10 +57,6 @@ struct DatabaseName {
pub async fn get_database_list(conn: &mut MySqlConnection) -> anyhow::Result<Vec<String>> { pub async fn get_database_list(conn: &mut MySqlConnection) -> anyhow::Result<Vec<String>> {
let unix_user = get_current_unix_user()?; let unix_user = get_current_unix_user()?;
let unix_groups = get_unix_groups(&unix_user)?
.into_iter()
.map(|g| g.name)
.collect::<Vec<_>>();
let databases = sqlx::query_as::<_, DatabaseName>( let databases = sqlx::query_as::<_, DatabaseName>(
r#" r#"
@ -69,11 +66,7 @@ pub async fn get_database_list(conn: &mut MySqlConnection) -> anyhow::Result<Vec
AND `SCHEMA_NAME` REGEXP ? AND `SCHEMA_NAME` REGEXP ?
"#, "#,
) )
.bind(format!( .bind(create_user_group_matching_regex(&unix_user))
"({}|{})_.+",
unix_user.name,
unix_groups.iter().map(|g| g.to_string()).join("|")
))
.fetch_all(conn) .fetch_all(conn)
.await .await
.context(format!( .context(format!(
@ -241,10 +234,6 @@ pub async fn get_all_database_privileges(
conn: &mut MySqlConnection, conn: &mut MySqlConnection,
) -> anyhow::Result<Vec<DatabasePrivileges>> { ) -> anyhow::Result<Vec<DatabasePrivileges>> {
let unix_user = get_current_unix_user()?; let unix_user = get_current_unix_user()?;
let unix_groups = get_unix_groups(&unix_user)?
.into_iter()
.map(|g| g.name)
.collect::<Vec<_>>();
let result = sqlx::query_as::<_, DatabasePrivileges>(&format!( let result = sqlx::query_as::<_, DatabasePrivileges>(&format!(
indoc! {r#" indoc! {r#"
@ -259,11 +248,7 @@ pub async fn get_all_database_privileges(
.map(|field| format!("`{field}`")) .map(|field| format!("`{field}`"))
.join(","), .join(","),
)) ))
.bind(format!( .bind(create_user_group_matching_regex(&unix_user))
"({}|{})_.+",
unix_user.name,
unix_groups.iter().map(|g| g.to_string()).join("|")
))
.fetch_all(conn) .fetch_all(conn)
.await .await
.context("Failed to show databases")?; .context("Failed to show databases")?;

View File

@ -6,7 +6,7 @@ use sqlx::{prelude::*, MySqlConnection};
use crate::core::common::quote_literal; use crate::core::common::quote_literal;
use super::common::{get_current_unix_user, get_unix_groups, validate_prefix_for_user}; use super::common::{create_user_group_matching_regex, get_current_unix_user, validate_prefix_for_user};
pub async fn create_database_user(db_user: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> { pub async fn create_database_user(db_user: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> {
let unix_user = get_current_unix_user()?; let unix_user = get_current_unix_user()?;
@ -71,22 +71,10 @@ pub struct DatabaseUser {
pub authentication_string: String, pub authentication_string: String,
} }
pub async fn get_all_database_users_for_user( pub async fn get_all_database_users_for_unix_user(
user: &User, unix_user: &User,
conn: &mut MySqlConnection, conn: &mut MySqlConnection,
) -> anyhow::Result<Vec<DatabaseUser>> { ) -> anyhow::Result<Vec<DatabaseUser>> {
let groups = get_unix_groups(user)?;
let regex = format!(
"({}|{})(_.+)?",
user.name,
groups
.iter()
.map(|g| g.name.as_str())
.collect::<Vec<_>>()
.join("|")
);
let users = sqlx::query_as::<_, DatabaseUser>( let users = sqlx::query_as::<_, DatabaseUser>(
r#" r#"
SELECT `User`, `Host`, `Password`, `authentication_string` SELECT `User`, `Host`, `Password`, `authentication_string`
@ -94,7 +82,7 @@ pub async fn get_all_database_users_for_user(
WHERE `User` REGEXP ? WHERE `User` REGEXP ?
"#, "#,
) )
.bind(regex) .bind(create_user_group_matching_regex(unix_user))
.fetch_all(conn) .fetch_all(conn)
.await?; .await?;