Fix binary collation issues for privs as well

Ref #66
This commit is contained in:
Oystein Kristoffer Tveit 2024-08-19 17:44:21 +02:00
parent f43499fca0
commit 20669569f3
Signed by: oysteikt
GPG Key ID: 9F2F7D8250F35146
4 changed files with 31 additions and 27 deletions

View File

@ -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)),

View File

@ -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<String, sqlx::Error> {
row.try_get(column).or_else(|_| {
row.try_get::<Vec<u8>, _>(column)
.map(|v| String::from_utf8_lossy(&v).to_string())
})
}

View File

@ -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<bool, sqlx::Error> {
let field = DATABASE_PRIVILEGE_FIELDS[position];
@ -113,8 +115,8 @@ fn get_mysql_row_priv_field(row: &MySqlRow, position: usize) -> Result<bool, sql
impl FromRow<'_, MySqlRow> for DatabasePrivilegeRow {
fn from_row(row: &MySqlRow) -> Result<Self, sqlx::Error> {
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<Vec<DatabasePrivilegeRow>, 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<Option<DatabasePrivilegeRow>, 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)

View File

@ -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<String>,
}
/// 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<String, sqlx::Error> {
row.try_get(column).or_else(|_| {
row.try_get::<Vec<u8>, _>(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<Self, sqlx::Error> {
Ok(Self {