From 20669569f34bc6d61675a8e1edd383b79709ecb4 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Mon, 19 Aug 2024 17:44:21 +0200 Subject: [PATCH] Fix binary collation issues for privs as well Ref #66 --- src/core/database_privileges.rs | 8 ++++---- src/server/common.rs | 15 ++++++++++++++ .../sql/database_privilege_operations.rs | 20 ++++++++++--------- src/server/sql/user_operations.rs | 15 +------------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/core/database_privileges.rs b/src/core/database_privileges.rs index 847108f..696bfd6 100644 --- a/src/core/database_privileges.rs +++ b/src/core/database_privileges.rs @@ -14,8 +14,8 @@ use crate::server::sql::database_privilege_operations::{ pub fn db_priv_field_human_readable_name(name: &str) -> String { match name { - "db" => "Database".to_owned(), - "user" => "User".to_owned(), + "Db" => "Database".to_owned(), + "User" => "User".to_owned(), "select_priv" => "Select".to_owned(), "insert_priv" => "Insert".to_owned(), "update_priv" => "Update".to_owned(), @@ -128,8 +128,8 @@ pub fn format_privileges_line_for_editor( DATABASE_PRIVILEGE_FIELDS .into_iter() .map(|field| match field { - "db" => format!("{:width$}", privs.db, width = database_name_len), - "user" => format!("{:width$}", privs.user, width = username_len), + "Db" => format!("{:width$}", privs.db, width = database_name_len), + "User" => format!("{:width$}", privs.user, width = username_len), privilege => format!( "{:width$}", yn(privs.get_privilege_by_name(privilege)), diff --git a/src/server/common.rs b/src/server/common.rs index db540ce..fb39122 100644 --- a/src/server/common.rs +++ b/src/server/common.rs @@ -1,4 +1,5 @@ use crate::core::common::UnixUser; +use sqlx::prelude::*; /// This function creates a regex that matches items (users, databases) /// that belong to the user or any of the user's groups. @@ -9,3 +10,17 @@ pub fn create_user_group_matching_regex(user: &UnixUser) -> String { format!("({}|{})(_.+)?", user.username, user.groups.join("|")) } } + +/// Some mysql versions with some collations mark some columns as binary fields, +/// which in the current version of sqlx is not parsable as string. +/// See: https://github.com/launchbadge/sqlx/issues/3387 +#[inline] +pub fn try_get_with_binary_fallback( + row: &sqlx::mysql::MySqlRow, + column: &str, +) -> Result { + row.try_get(column).or_else(|_| { + row.try_get::, _>(column) + .map(|v| String::from_utf8_lossy(&v).to_string()) + }) +} \ No newline at end of file diff --git a/src/server/sql/database_privilege_operations.rs b/src/server/sql/database_privilege_operations.rs index ec6b776..a9d1dc5 100644 --- a/src/server/sql/database_privilege_operations.rs +++ b/src/server/sql/database_privilege_operations.rs @@ -32,7 +32,7 @@ use crate::{ }, }, server::{ - common::create_user_group_matching_regex, + common::{create_user_group_matching_regex, try_get_with_binary_fallback}, input_sanitization::{quote_identifier, validate_name, validate_ownership_by_unix_user}, sql::database_operations::unsafe_database_exists, }, @@ -42,8 +42,8 @@ use crate::{ /// from the `db` table in the database. If you need to add or remove privilege /// fields, this is a good place to start. pub const DATABASE_PRIVILEGE_FIELDS: [&str; 13] = [ - "db", - "user", + "Db", + "User", "select_priv", "insert_priv", "update_priv", @@ -97,6 +97,8 @@ impl DatabasePrivilegeRow { } } +// TODO: get by name instead of row tuple position + #[inline] fn get_mysql_row_priv_field(row: &MySqlRow, position: usize) -> Result { let field = DATABASE_PRIVILEGE_FIELDS[position]; @@ -113,8 +115,8 @@ fn get_mysql_row_priv_field(row: &MySqlRow, position: usize) -> Result for DatabasePrivilegeRow { fn from_row(row: &MySqlRow) -> Result { Ok(Self { - db: row.try_get("db")?, - user: row.try_get("user")?, + db: try_get_with_binary_fallback(row, "Db")?, + user: try_get_with_binary_fallback(row, "User")?, select_priv: get_mysql_row_priv_field(row, 2)?, insert_priv: get_mysql_row_priv_field(row, 3)?, update_priv: get_mysql_row_priv_field(row, 4)?, @@ -137,7 +139,7 @@ async fn unsafe_get_database_privileges( connection: &mut MySqlConnection, ) -> Result, sqlx::Error> { let result = sqlx::query_as::<_, DatabasePrivilegeRow>(&format!( - "SELECT {} FROM `db` WHERE `db` = ?", + "SELECT {} FROM `db` WHERE `Db` = ?", DATABASE_PRIVILEGE_FIELDS .iter() .map(|field| quote_identifier(field)) @@ -166,7 +168,7 @@ pub async fn unsafe_get_database_privileges_for_db_user_pair( connection: &mut MySqlConnection, ) -> Result, sqlx::Error> { let result = sqlx::query_as::<_, DatabasePrivilegeRow>(&format!( - "SELECT {} FROM `db` WHERE `db` = ? AND `user` = ?", + "SELECT {} FROM `db` WHERE `Db` = ? AND `User` = ?", DATABASE_PRIVILEGE_FIELDS .iter() .map(|field| quote_identifier(field)) @@ -316,7 +318,7 @@ async fn unsafe_apply_privilege_diff( .join(","); sqlx::query( - format!("UPDATE `db` SET {} WHERE `db` = ? AND `user` = ?", changes).as_str(), + format!("UPDATE `db` SET {} WHERE `Db` = ? AND `User` = ?", changes).as_str(), ) .bind(p.db.to_string()) .bind(p.user.to_string()) @@ -325,7 +327,7 @@ async fn unsafe_apply_privilege_diff( .map(|_| ()) } DatabasePrivilegesDiff::Deleted(p) => { - sqlx::query("DELETE FROM `db` WHERE `db` = ? AND `user` = ?") + sqlx::query("DELETE FROM `db` WHERE `Db` = ? AND `User` = ?") .bind(p.db.to_string()) .bind(p.user.to_string()) .execute(connection) diff --git a/src/server/sql/user_operations.rs b/src/server/sql/user_operations.rs index 06da219..120903b 100644 --- a/src/server/sql/user_operations.rs +++ b/src/server/sql/user_operations.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use sqlx::prelude::*; use sqlx::MySqlConnection; +use crate::server::common::try_get_with_binary_fallback; use crate::{ core::{ common::UnixUser, @@ -350,20 +351,6 @@ pub struct DatabaseUser { pub databases: Vec, } -/// Some mysql versions with some collations mark some columns as binary fields, -/// which in the current version of sqlx is not parsable as string. -/// See: https://github.com/launchbadge/sqlx/issues/3387 -#[inline] -fn try_get_with_binary_fallback( - row: &sqlx::mysql::MySqlRow, - column: &str, -) -> Result { - row.try_get(column).or_else(|_| { - row.try_get::, _>(column) - .map(|v| String::from_utf8_lossy(&v).to_string()) - }) -} - impl FromRow<'_, sqlx::mysql::MySqlRow> for DatabaseUser { fn from_row(row: &sqlx::mysql::MySqlRow) -> Result { Ok(Self {