From bf6027f50735d1c7543834de06358fcb246dadd8 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Mon, 15 Dec 2025 14:25:22 +0900 Subject: [PATCH] core/protocol: use thiserror, use common authorization error struct --- Cargo.lock | 1 + Cargo.toml | 1 + .../error_messages.rs | 35 ++++---- .../protocol/commands/check_authorization.rs | 39 ++------- .../protocol/commands/create_databases.rs | 25 +++--- src/core/protocol/commands/create_users.rs | 23 +++-- src/core/protocol/commands/drop_databases.rs | 25 +++--- src/core/protocol/commands/drop_users.rs | 23 +++-- .../protocol/commands/list_all_databases.rs | 4 +- .../protocol/commands/list_all_privileges.rs | 4 +- src/core/protocol/commands/list_all_users.rs | 4 +- src/core/protocol/commands/list_databases.rs | 25 +++--- src/core/protocol/commands/list_privileges.rs | 25 +++--- src/core/protocol/commands/list_users.rs | 23 +++-- src/core/protocol/commands/lock_users.rs | 25 +++--- .../protocol/commands/modify_privileges.rs | 54 ++++++------ src/core/protocol/commands/passwd_user.rs | 25 +++--- src/core/protocol/commands/unlock_users.rs | 25 +++--- src/core/protocol/request_validation.rs | 62 ++++++++++++-- src/server/authorization.rs | 26 +++--- src/server/sql/database_operations.rs | 61 +++++++------- .../sql/database_privilege_operations.rs | 66 +++++++-------- src/server/sql/user_operations.rs | 83 +++++++++++++------ 23 files changed, 367 insertions(+), 317 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef333de..a038071 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1189,6 +1189,7 @@ dependencies = [ "serde", "serde_json", "sqlx", + "thiserror 2.0.17", "tokio", "tokio-serde", "tokio-stream", diff --git a/Cargo.toml b/Cargo.toml index 3ba8bff..0e195e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ sd-notify = "0.4.5" serde = "1.0.228" serde_json = { version = "1.0.145", features = ["preserve_order"] } sqlx = { version = "0.8.6", features = ["runtime-tokio", "mysql", "tls-rustls"] } +thiserror = "2.0.17" tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "signal"] } tokio-serde = { version = "0.9.0", features = ["bincode"] } tokio-stream = "0.1.17" diff --git a/src/client/mysql_admutils_compatibility/error_messages.rs b/src/client/mysql_admutils_compatibility/error_messages.rs index fa393c4..3378e6a 100644 --- a/src/client/mysql_admutils_compatibility/error_messages.rs +++ b/src/client/mysql_admutils_compatibility/error_messages.rs @@ -1,7 +1,7 @@ use crate::core::{ protocol::{ CreateDatabaseError, CreateUserError, DropDatabaseError, DropUserError, - GetDatabasesPrivilegeDataError, ListUsersError, + GetDatabasesPrivilegeDataError, ListUsersError, request_validation::AuthorizationError, }, types::DbOrUser, }; @@ -36,13 +36,13 @@ pub fn handle_create_user_error(error: CreateUserError, name: &str) { .next() .unwrap_or_else(|| "mysql-useradm".to_string()); match error { - CreateUserError::SanitizationError(_) => { + CreateUserError::AuthorizationError(AuthorizationError::SanitizationError(_)) => { eprintln!( "{}", name_validation_error_to_error_message(DbOrUser::User(name.into())) ); } - CreateUserError::OwnershipError(_) => { + CreateUserError::AuthorizationError(AuthorizationError::OwnershipError(_)) => { eprintln!( "{}", owner_validation_error_message(DbOrUser::User(name.into())) @@ -59,13 +59,13 @@ pub fn handle_drop_user_error(error: DropUserError, name: &str) { .next() .unwrap_or_else(|| "mysql-useradm".to_string()); match error { - DropUserError::SanitizationError(_) => { + DropUserError::AuthorizationError(AuthorizationError::SanitizationError(_)) => { eprintln!( "{}", name_validation_error_to_error_message(DbOrUser::User(name.into())) ); } - DropUserError::OwnershipError(_) => { + DropUserError::AuthorizationError(AuthorizationError::OwnershipError(_)) => { eprintln!( "{}", owner_validation_error_message(DbOrUser::User(name.into())) @@ -82,13 +82,13 @@ pub fn handle_list_users_error(error: ListUsersError, name: &str) { .next() .unwrap_or_else(|| "mysql-useradm".to_string()); match error { - ListUsersError::SanitizationError(_) => { + ListUsersError::AuthorizationError(AuthorizationError::SanitizationError(_)) => { eprintln!( "{}", name_validation_error_to_error_message(DbOrUser::User(name.into())) ); } - ListUsersError::OwnershipError(_) => { + ListUsersError::AuthorizationError(AuthorizationError::OwnershipError(_)) => { eprintln!( "{}", owner_validation_error_message(DbOrUser::User(name.into())) @@ -113,13 +113,14 @@ pub fn handle_create_database_error(error: CreateDatabaseError, name: &str) { .next() .unwrap_or_else(|| "mysql-dbadm".to_string()); match error { - CreateDatabaseError::SanitizationError(_) => { + CreateDatabaseError::AuthorizationError(AuthorizationError::SanitizationError(_)) => { eprintln!( "{}", name_validation_error_to_error_message(DbOrUser::Database(name.into())) ); } - CreateDatabaseError::OwnershipError(_) => { + + CreateDatabaseError::AuthorizationError(AuthorizationError::OwnershipError(_)) => { eprintln!( "{}", owner_validation_error_message(DbOrUser::Database(name.into())) @@ -139,13 +140,13 @@ pub fn handle_drop_database_error(error: DropDatabaseError, name: &str) { .next() .unwrap_or_else(|| "mysql-dbadm".to_string()); match error { - DropDatabaseError::SanitizationError(_) => { + DropDatabaseError::AuthorizationError(AuthorizationError::SanitizationError(_)) => { eprintln!( "{}", name_validation_error_to_error_message(DbOrUser::Database(name.into())) ); } - DropDatabaseError::OwnershipError(_) => { + DropDatabaseError::AuthorizationError(AuthorizationError::OwnershipError(_)) => { eprintln!( "{}", owner_validation_error_message(DbOrUser::Database(name.into())) @@ -169,12 +170,12 @@ pub fn format_show_database_error_message( .unwrap_or_else(|| "mysql-dbadm".to_string()); match error { - GetDatabasesPrivilegeDataError::SanitizationError(_) => { - name_validation_error_to_error_message(DbOrUser::Database(name.into())) - } - GetDatabasesPrivilegeDataError::OwnershipError(_) => { - owner_validation_error_message(DbOrUser::Database(name.into())) - } + GetDatabasesPrivilegeDataError::AuthorizationError( + AuthorizationError::SanitizationError(_), + ) => name_validation_error_to_error_message(DbOrUser::Database(name.into())), + GetDatabasesPrivilegeDataError::AuthorizationError(AuthorizationError::OwnershipError( + _, + )) => owner_validation_error_message(DbOrUser::Database(name.into())), GetDatabasesPrivilegeDataError::MySqlError(err) => { format!( "{}: Failed to look up privileges for database '{}': {}", diff --git a/src/core/protocol/commands/check_authorization.rs b/src/core/protocol/commands/check_authorization.rs index f632700..de42811 100644 --- a/src/core/protocol/commands/check_authorization.rs +++ b/src/core/protocol/commands/check_authorization.rs @@ -2,22 +2,17 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; -use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, - types::DbOrUser, -}; +use crate::core::{protocol::request_validation::AuthorizationError, types::DbOrUser}; pub type CheckAuthorizationRequest = Vec; pub type CheckAuthorizationResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum CheckAuthorizationError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), - // AuthorizationHandlerError(String), -} +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[error("Authorization error: {0}")] +pub struct CheckAuthorizationError(#[from] pub AuthorizationError); pub fn print_check_authorization_output_status(output: &CheckAuthorizationResponse) { for (db_or_user, result) in output { @@ -63,30 +58,10 @@ pub fn print_check_authorization_output_status_json(output: &CheckAuthorizationR impl CheckAuthorizationError { pub fn to_error_message(&self, db_or_user: &DbOrUser) -> String { - match self { - CheckAuthorizationError::SanitizationError(err) => { - err.to_error_message(db_or_user.clone()) - } - CheckAuthorizationError::OwnershipError(err) => { - err.to_error_message(db_or_user.clone()) - } // CheckAuthorizationError::AuthorizationHandlerError(msg) => { - // format!( - // "Authorization handler error for '{}': {}", - // db_or_user.name(), - // msg - // ) - // } - } + self.0.to_error_message(db_or_user.clone()) } pub fn error_type(&self) -> String { - match self { - CheckAuthorizationError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - CheckAuthorizationError::OwnershipError(err) => { - format!("ownership-error/{}", err.error_type()) - } // CheckAuthorizationError::AuthorizationHandlerError(_) => "authorization-handler-error".to_string(), - } + self.0.error_type() } } diff --git a/src/core/protocol/commands/create_databases.rs b/src/core/protocol/commands/create_databases.rs index 0fb5a2f..2d6ee9b 100644 --- a/src/core/protocol/commands/create_databases.rs +++ b/src/core/protocol/commands/create_databases.rs @@ -2,9 +2,10 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLDatabase}, }; @@ -12,11 +13,15 @@ pub type CreateDatabasesRequest = Vec; pub type CreateDatabasesResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum CreateDatabaseError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("Database already exists")] DatabaseAlreadyExists, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -60,10 +65,7 @@ pub fn print_create_databases_output_status_json(output: &CreateDatabasesRespons impl CreateDatabaseError { pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String { match self { - CreateDatabaseError::SanitizationError(err) => { - err.to_error_message(DbOrUser::Database(database_name.clone())) - } - CreateDatabaseError::OwnershipError(err) => { + CreateDatabaseError::AuthorizationError(err) => { err.to_error_message(DbOrUser::Database(database_name.clone())) } CreateDatabaseError::DatabaseAlreadyExists => { @@ -77,12 +79,7 @@ impl CreateDatabaseError { pub fn error_type(&self) -> String { match self { - CreateDatabaseError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - CreateDatabaseError::OwnershipError(err) => { - format!("ownership-error/{}", err.error_type()) - } + CreateDatabaseError::AuthorizationError(err) => err.error_type(), CreateDatabaseError::DatabaseAlreadyExists => "database-already-exists".to_string(), CreateDatabaseError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/create_users.rs b/src/core/protocol/commands/create_users.rs index 6991bfd..b703375 100644 --- a/src/core/protocol/commands/create_users.rs +++ b/src/core/protocol/commands/create_users.rs @@ -2,9 +2,10 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLUser}, }; @@ -12,11 +13,15 @@ pub type CreateUsersRequest = Vec; pub type CreateUsersResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum CreateUserError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("User already exists")] UserAlreadyExists, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -60,10 +65,7 @@ pub fn print_create_users_output_status_json(output: &CreateUsersResponse) { impl CreateUserError { pub fn to_error_message(&self, username: &MySQLUser) -> String { match self { - CreateUserError::SanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - CreateUserError::OwnershipError(err) => { + CreateUserError::AuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } CreateUserError::UserAlreadyExists => { @@ -77,10 +79,7 @@ impl CreateUserError { pub fn error_type(&self) -> String { match self { - CreateUserError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - CreateUserError::OwnershipError(err) => format!("ownership-error/{}", err.error_type()), + CreateUserError::AuthorizationError(err) => err.error_type(), CreateUserError::UserAlreadyExists => "user-already-exists".to_string(), CreateUserError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/drop_databases.rs b/src/core/protocol/commands/drop_databases.rs index 724b9e2..d9ff408 100644 --- a/src/core/protocol/commands/drop_databases.rs +++ b/src/core/protocol/commands/drop_databases.rs @@ -2,9 +2,10 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLDatabase}, }; @@ -12,11 +13,15 @@ pub type DropDatabasesRequest = Vec; pub type DropDatabasesResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum DropDatabaseError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("Database does not exist")] DatabaseDoesNotExist, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -63,10 +68,7 @@ pub fn print_drop_databases_output_status_json(output: &DropDatabasesResponse) { impl DropDatabaseError { pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String { match self { - DropDatabaseError::SanitizationError(err) => { - err.to_error_message(DbOrUser::Database(database_name.clone())) - } - DropDatabaseError::OwnershipError(err) => { + DropDatabaseError::AuthorizationError(err) => { err.to_error_message(DbOrUser::Database(database_name.clone())) } DropDatabaseError::DatabaseDoesNotExist => { @@ -80,12 +82,7 @@ impl DropDatabaseError { pub fn error_type(&self) -> String { match self { - DropDatabaseError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - DropDatabaseError::OwnershipError(err) => { - format!("ownership-error/{}", err.error_type()) - } + DropDatabaseError::AuthorizationError(err) => err.error_type(), DropDatabaseError::DatabaseDoesNotExist => "database-does-not-exist".to_string(), DropDatabaseError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/drop_users.rs b/src/core/protocol/commands/drop_users.rs index 74e5970..c4aeadd 100644 --- a/src/core/protocol/commands/drop_users.rs +++ b/src/core/protocol/commands/drop_users.rs @@ -2,9 +2,10 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLUser}, }; @@ -12,11 +13,15 @@ pub type DropUsersRequest = Vec; pub type DropUsersResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum DropUserError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("User does not exist")] UserDoesNotExist, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -60,10 +65,7 @@ pub fn print_drop_users_output_status_json(output: &DropUsersResponse) { impl DropUserError { pub fn to_error_message(&self, username: &MySQLUser) -> String { match self { - DropUserError::SanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - DropUserError::OwnershipError(err) => { + DropUserError::AuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } DropUserError::UserDoesNotExist => { @@ -77,10 +79,7 @@ impl DropUserError { pub fn error_type(&self) -> String { match self { - DropUserError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - DropUserError::OwnershipError(err) => format!("ownership-error/{}", err.error_type()), + DropUserError::AuthorizationError(err) => err.error_type(), DropUserError::UserDoesNotExist => "user-does-not-exist".to_string(), DropUserError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/list_all_databases.rs b/src/core/protocol/commands/list_all_databases.rs index e146bb9..a4a8b1c 100644 --- a/src/core/protocol/commands/list_all_databases.rs +++ b/src/core/protocol/commands/list_all_databases.rs @@ -1,11 +1,13 @@ use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::server::sql::database_operations::DatabaseRow; pub type ListAllDatabasesResponse = Result, ListAllDatabasesError>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ListAllDatabasesError { + #[error("MySQL error: {0}")] MySqlError(String), } diff --git a/src/core/protocol/commands/list_all_privileges.rs b/src/core/protocol/commands/list_all_privileges.rs index 86f094a..5e53c27 100644 --- a/src/core/protocol/commands/list_all_privileges.rs +++ b/src/core/protocol/commands/list_all_privileges.rs @@ -1,12 +1,14 @@ use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::core::database_privileges::DatabasePrivilegeRow; pub type ListAllPrivilegesResponse = Result, GetAllDatabasesPrivilegeDataError>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum GetAllDatabasesPrivilegeDataError { + #[error("MySQL error: {0}")] MySqlError(String), } diff --git a/src/core/protocol/commands/list_all_users.rs b/src/core/protocol/commands/list_all_users.rs index 53b1523..38f3071 100644 --- a/src/core/protocol/commands/list_all_users.rs +++ b/src/core/protocol/commands/list_all_users.rs @@ -1,11 +1,13 @@ use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::server::sql::user_operations::DatabaseUser; pub type ListAllUsersResponse = Result, ListAllUsersError>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ListAllUsersError { + #[error("MySQL error: {0}")] MySqlError(String), } diff --git a/src/core/protocol/commands/list_databases.rs b/src/core/protocol/commands/list_databases.rs index 9e1ca7b..f268606 100644 --- a/src/core/protocol/commands/list_databases.rs +++ b/src/core/protocol/commands/list_databases.rs @@ -4,10 +4,11 @@ use itertools::Itertools; use prettytable::Table; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::{ core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLDatabase}, }, server::sql::database_operations::DatabaseRow, @@ -17,11 +18,15 @@ pub type ListDatabasesRequest = Option>; pub type ListDatabasesResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ListDatabasesError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("Database does not exist")] DatabaseDoesNotExist, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -99,10 +104,7 @@ pub fn print_list_databases_output_status_json(output: &ListDatabasesResponse) { impl ListDatabasesError { pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String { match self { - ListDatabasesError::SanitizationError(err) => { - err.to_error_message(DbOrUser::Database(database_name.clone())) - } - ListDatabasesError::OwnershipError(err) => { + ListDatabasesError::AuthorizationError(err) => { err.to_error_message(DbOrUser::Database(database_name.clone())) } ListDatabasesError::DatabaseDoesNotExist => { @@ -116,12 +118,7 @@ impl ListDatabasesError { pub fn error_type(&self) -> String { match self { - ListDatabasesError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - ListDatabasesError::OwnershipError(err) => { - format!("ownership-error/{}", err.error_type()) - } + ListDatabasesError::AuthorizationError(err) => err.error_type(), ListDatabasesError::DatabaseDoesNotExist => "database-does-not-exist".to_string(), ListDatabasesError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/list_privileges.rs b/src/core/protocol/commands/list_privileges.rs index 02588b7..daad53b 100644 --- a/src/core/protocol/commands/list_privileges.rs +++ b/src/core/protocol/commands/list_privileges.rs @@ -8,6 +8,7 @@ use itertools::Itertools; use prettytable::{Cell, Row, Table}; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ common::yn, @@ -15,7 +16,7 @@ use crate::core::{ DATABASE_PRIVILEGE_FIELDS, DatabasePrivilegeRow, db_priv_field_human_readable_name, db_priv_field_single_character_name, }, - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLDatabase}, }; @@ -115,21 +116,22 @@ pub fn print_list_privileges_output_status_json(output: &ListPrivilegesResponse) ); } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum GetDatabasesPrivilegeDataError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("Database does not exist")] DatabaseDoesNotExist, + + #[error("MySQL error: {0}")] MySqlError(String), } impl GetDatabasesPrivilegeDataError { pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String { match self { - GetDatabasesPrivilegeDataError::SanitizationError(err) => { - err.to_error_message(DbOrUser::Database(database_name.clone())) - } - GetDatabasesPrivilegeDataError::OwnershipError(err) => { + GetDatabasesPrivilegeDataError::AuthorizationError(err) => { err.to_error_message(DbOrUser::Database(database_name.clone())) } GetDatabasesPrivilegeDataError::DatabaseDoesNotExist => { @@ -143,12 +145,7 @@ impl GetDatabasesPrivilegeDataError { pub fn error_type(&self) -> String { match self { - GetDatabasesPrivilegeDataError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - GetDatabasesPrivilegeDataError::OwnershipError(err) => { - format!("ownership-error/{}", err.error_type()) - } + GetDatabasesPrivilegeDataError::AuthorizationError(err) => err.error_type(), GetDatabasesPrivilegeDataError::DatabaseDoesNotExist => { "database-does-not-exist".to_string() } diff --git a/src/core/protocol/commands/list_users.rs b/src/core/protocol/commands/list_users.rs index 09e2f98..fc207f6 100644 --- a/src/core/protocol/commands/list_users.rs +++ b/src/core/protocol/commands/list_users.rs @@ -3,10 +3,11 @@ use std::collections::BTreeMap; use prettytable::Table; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::{ core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLUser}, }, server::sql::user_operations::DatabaseUser, @@ -16,11 +17,15 @@ pub type ListUsersRequest = Option>; pub type ListUsersResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ListUsersError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("User does not exist")] UserDoesNotExist, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -94,10 +99,7 @@ pub fn print_list_users_output_status_json(output: &ListUsersResponse) { impl ListUsersError { pub fn to_error_message(&self, username: &MySQLUser) -> String { match self { - ListUsersError::SanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - ListUsersError::OwnershipError(err) => { + ListUsersError::AuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } ListUsersError::UserDoesNotExist => { @@ -111,10 +113,7 @@ impl ListUsersError { pub fn error_type(&self) -> String { match self { - ListUsersError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - ListUsersError::OwnershipError(err) => format!("ownership-error/{}", err.error_type()), + ListUsersError::AuthorizationError(err) => err.error_type(), ListUsersError::UserDoesNotExist => "user-does-not-exist".to_string(), ListUsersError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/lock_users.rs b/src/core/protocol/commands/lock_users.rs index facae24..d0ab673 100644 --- a/src/core/protocol/commands/lock_users.rs +++ b/src/core/protocol/commands/lock_users.rs @@ -2,9 +2,10 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLUser}, }; @@ -12,12 +13,18 @@ pub type LockUsersRequest = Vec; pub type LockUsersResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum LockUserError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("User does not exist")] UserDoesNotExist, + + #[error("User is already locked")] UserIsAlreadyLocked, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -61,10 +68,7 @@ pub fn print_lock_users_output_status_json(output: &LockUsersResponse) { impl LockUserError { pub fn to_error_message(&self, username: &MySQLUser) -> String { match self { - LockUserError::SanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - LockUserError::OwnershipError(err) => { + LockUserError::AuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } LockUserError::UserDoesNotExist => { @@ -81,10 +85,7 @@ impl LockUserError { pub fn error_type(&self) -> String { match self { - LockUserError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - LockUserError::OwnershipError(err) => format!("ownership-error/{}", err.error_type()), + LockUserError::AuthorizationError(err) => err.error_type(), LockUserError::UserDoesNotExist => "user-does-not-exist".to_string(), LockUserError::UserIsAlreadyLocked => "user-is-already-locked".to_string(), LockUserError::MySqlError(_) => "mysql-error".to_string(), diff --git a/src/core/protocol/commands/modify_privileges.rs b/src/core/protocol/commands/modify_privileges.rs index 49d10e5..98f94a3 100644 --- a/src/core/protocol/commands/modify_privileges.rs +++ b/src/core/protocol/commands/modify_privileges.rs @@ -1,10 +1,11 @@ use std::collections::{BTreeMap, BTreeSet}; use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::core::{ database_privileges::{DatabasePrivilegeRow, DatabasePrivilegeRowDiff, DatabasePrivilegesDiff}, - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLDatabase, MySQLUser}, }; @@ -13,23 +14,37 @@ pub type ModifyPrivilegesRequest = BTreeSet; pub type ModifyPrivilegesResponse = BTreeMap<(MySQLDatabase, MySQLUser), Result<(), ModifyDatabasePrivilegesError>>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ModifyDatabasePrivilegesError { - DatabaseSanitizationError(NameValidationError), - DatabaseOwnershipError(OwnerValidationError), - UserSanitizationError(NameValidationError), - UserOwnershipError(OwnerValidationError), + #[error("Database authorization error: {0}")] + DatabaseAuthorizationError(AuthorizationError), + + #[error("User authorization error: {0}")] + UserAuthorizationError(AuthorizationError), + + #[error("Database does not exist")] DatabaseDoesNotExist, + + #[error("User does not exist")] UserDoesNotExist, + + #[error("Diff does not apply: {0}")] DiffDoesNotApply(DiffDoesNotApplyError), + + #[error("MySQL error: {0}")] MySqlError(String), } #[allow(clippy::enum_variant_names)] -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum DiffDoesNotApplyError { + #[error("Privileges row already exists for database '{0}' and user '{1}'")] RowAlreadyExists(MySQLDatabase, MySQLUser), + + #[error("Privileges row does not exist for database '{0}' and user '{1}'")] RowDoesNotExist(MySQLDatabase, MySQLUser), + + #[error("Privilege change '{0:?}' does not apply to row '{1:?}'")] RowPrivilegeChangeDoesNotApply(DatabasePrivilegeRowDiff, DatabasePrivilegeRow), } @@ -54,16 +69,10 @@ pub fn print_modify_database_privileges_output_status(output: &ModifyPrivilegesR impl ModifyDatabasePrivilegesError { pub fn to_error_message(&self, database_name: &MySQLDatabase, username: &MySQLUser) -> String { match self { - ModifyDatabasePrivilegesError::DatabaseSanitizationError(err) => { + ModifyDatabasePrivilegesError::DatabaseAuthorizationError(err) => { err.to_error_message(DbOrUser::Database(database_name.clone())) } - ModifyDatabasePrivilegesError::DatabaseOwnershipError(err) => { - err.to_error_message(DbOrUser::Database(database_name.clone())) - } - ModifyDatabasePrivilegesError::UserSanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - ModifyDatabasePrivilegesError::UserOwnershipError(err) => { + ModifyDatabasePrivilegesError::UserAuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } ModifyDatabasePrivilegesError::DatabaseDoesNotExist => { @@ -87,18 +96,9 @@ impl ModifyDatabasePrivilegesError { #[allow(dead_code)] pub fn error_type(&self) -> String { match self { - ModifyDatabasePrivilegesError::DatabaseSanitizationError(err) => { - format!("database-sanitization-error/{}", err.error_type()) - } - ModifyDatabasePrivilegesError::DatabaseOwnershipError(err) => { - format!("database-ownership-error/{}", err.error_type()) - } - ModifyDatabasePrivilegesError::UserSanitizationError(err) => { - format!("user-sanitization-error/{}", err.error_type()) - } - ModifyDatabasePrivilegesError::UserOwnershipError(err) => { - format!("user-ownership-error/{}", err.error_type()) - } + // TODO: should these be subtyped? + ModifyDatabasePrivilegesError::DatabaseAuthorizationError(err) => err.error_type(), + ModifyDatabasePrivilegesError::UserAuthorizationError(err) => err.error_type(), ModifyDatabasePrivilegesError::DatabaseDoesNotExist => { "database-does-not-exist".to_string() } diff --git a/src/core/protocol/commands/passwd_user.rs b/src/core/protocol/commands/passwd_user.rs index 09a7bff..8ba861e 100644 --- a/src/core/protocol/commands/passwd_user.rs +++ b/src/core/protocol/commands/passwd_user.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLUser}, }; @@ -9,11 +10,15 @@ pub type SetUserPasswordRequest = (MySQLUser, String); pub type SetUserPasswordResponse = Result<(), SetPasswordError>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SetPasswordError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("User does not exist")] UserDoesNotExist, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -32,10 +37,7 @@ pub fn print_set_password_output_status(output: &SetUserPasswordResponse, userna impl SetPasswordError { pub fn to_error_message(&self, username: &MySQLUser) -> String { match self { - SetPasswordError::SanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - SetPasswordError::OwnershipError(err) => { + SetPasswordError::AuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } SetPasswordError::UserDoesNotExist => { @@ -50,12 +52,7 @@ impl SetPasswordError { #[allow(dead_code)] pub fn error_type(&self) -> String { match self { - SetPasswordError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - SetPasswordError::OwnershipError(err) => { - format!("ownership-error/{}", err.error_type()) - } + SetPasswordError::AuthorizationError(err) => err.error_type(), SetPasswordError::UserDoesNotExist => "user-does-not-exist".to_string(), SetPasswordError::MySqlError(_) => "mysql-error".to_string(), } diff --git a/src/core/protocol/commands/unlock_users.rs b/src/core/protocol/commands/unlock_users.rs index 36b320d..b3be99a 100644 --- a/src/core/protocol/commands/unlock_users.rs +++ b/src/core/protocol/commands/unlock_users.rs @@ -2,9 +2,10 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serde_json::json; +use thiserror::Error; use crate::core::{ - protocol::request_validation::{NameValidationError, OwnerValidationError}, + protocol::request_validation::AuthorizationError, types::{DbOrUser, MySQLUser}, }; @@ -12,12 +13,18 @@ pub type UnlockUsersRequest = Vec; pub type UnlockUsersResponse = BTreeMap>; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum UnlockUserError { - SanitizationError(NameValidationError), - OwnershipError(OwnerValidationError), + #[error("Authorization error: {0}")] + AuthorizationError(#[from] AuthorizationError), + + #[error("User does not exist")] UserDoesNotExist, + + #[error("User is already unlocked")] UserIsAlreadyUnlocked, + + #[error("MySQL error: {0}")] MySqlError(String), } @@ -61,10 +68,7 @@ pub fn print_unlock_users_output_status_json(output: &UnlockUsersResponse) { impl UnlockUserError { pub fn to_error_message(&self, username: &MySQLUser) -> String { match self { - UnlockUserError::SanitizationError(err) => { - err.to_error_message(DbOrUser::User(username.clone())) - } - UnlockUserError::OwnershipError(err) => { + UnlockUserError::AuthorizationError(err) => { err.to_error_message(DbOrUser::User(username.clone())) } UnlockUserError::UserDoesNotExist => { @@ -81,10 +85,7 @@ impl UnlockUserError { pub fn error_type(&self) -> String { match self { - UnlockUserError::SanitizationError(err) => { - format!("sanitization-error/{}", err.error_type()) - } - UnlockUserError::OwnershipError(err) => format!("ownership-error/{}", err.error_type()), + UnlockUserError::AuthorizationError(err) => err.error_type(), UnlockUserError::UserDoesNotExist => "user-does-not-exist".to_string(), UnlockUserError::UserIsAlreadyUnlocked => "user-is-already-unlocked".to_string(), UnlockUserError::MySqlError(_) => "mysql-error".to_string(), diff --git a/src/core/protocol/request_validation.rs b/src/core/protocol/request_validation.rs index 5e9d323..3e84791 100644 --- a/src/core/protocol/request_validation.rs +++ b/src/core/protocol/request_validation.rs @@ -1,13 +1,21 @@ use indoc::indoc; use itertools::Itertools; use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::core::{common::UnixUser, types::DbOrUser}; -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +#[derive(Error, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] pub enum NameValidationError { + #[error("Name cannot be empty.")] EmptyString, + + #[error( + "Name contains invalid characters. Only A-Z, a-z, 0-9, _ (underscore) and - (dash) are permitted." + )] InvalidCharacters, + + #[error("Name is too long. Maximum length is 64 characters.")] TooLong, } @@ -44,6 +52,15 @@ impl NameValidationError { } } +#[derive(Error, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum OwnerValidationError { + #[error("No matching owner prefix found")] + NoMatch, + + #[error("Name cannot be empty")] + StringEmpty, +} + impl OwnerValidationError { pub fn to_error_message(self, db_or_user: DbOrUser) -> String { let user = UnixUser::from_enviroment(); @@ -98,11 +115,42 @@ impl OwnerValidationError { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub enum OwnerValidationError { - // The name is valid, but none of the given prefixes matched the name - NoMatch, +#[derive(Error, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum AuthorizationError { + #[error("Sanitization error: {0}")] + SanitizationError(NameValidationError), - // The name is empty, which is invalid - StringEmpty, + #[error("Ownership error: {0}")] + OwnershipError(OwnerValidationError), + // AuthorizationHandlerError(String), +} + +impl AuthorizationError { + pub fn to_error_message(&self, db_or_user: DbOrUser) -> String { + match self { + AuthorizationError::SanitizationError(err) => err.to_error_message(db_or_user), + AuthorizationError::OwnershipError(err) => err.to_error_message(db_or_user), + // AuthorizationError::AuthorizationHandlerError(msg) => { + // format!( + // "Authorization handler error for '{}': {}", + // db_or_user.name(), + // msg + // ) + // } + } + } + + pub fn error_type(&self) -> String { + match self { + AuthorizationError::SanitizationError(err) => { + format!("sanitization-error/{}", err.error_type()) + } + // TODO: maybe rename this to authorization error? + AuthorizationError::OwnershipError(err) => { + format!("ownership-error/{}", err.error_type()) + } // AuthorizationError::AuthorizationHandlerError(_) => { + // "authorization-handler-error".to_string() + // } + } + } } diff --git a/src/server/authorization.rs b/src/server/authorization.rs index a32baa3..2c7694a 100644 --- a/src/server/authorization.rs +++ b/src/server/authorization.rs @@ -1,5 +1,9 @@ use crate::{ - core::{common::UnixUser, protocol::CheckAuthorizationError, types::DbOrUser}, + core::{ + common::UnixUser, + protocol::{CheckAuthorizationError, request_validation::AuthorizationError}, + types::DbOrUser, + }, server::input_sanitization::{validate_name, validate_ownership_by_unix_user}, }; @@ -10,19 +14,19 @@ pub async fn check_authorization( let mut results = std::collections::BTreeMap::new(); for db_or_user in dbs_or_users { - if let Err(err) = validate_name(db_or_user.name()) { - results.insert( - db_or_user.clone(), - Err(CheckAuthorizationError::SanitizationError(err)), - ); + if let Err(err) = validate_name(db_or_user.name()) + .map_err(AuthorizationError::SanitizationError) + .map_err(CheckAuthorizationError) + { + results.insert(db_or_user.clone(), Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(db_or_user.name(), unix_user) { - results.insert( - db_or_user.clone(), - Err(CheckAuthorizationError::OwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(db_or_user.name(), unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(CheckAuthorizationError) + { + results.insert(db_or_user.clone(), Err(err)); continue; } diff --git a/src/server/sql/database_operations.rs b/src/server/sql/database_operations.rs index 8a1ba39..1b8b6a2 100644 --- a/src/server/sql/database_operations.rs +++ b/src/server/sql/database_operations.rs @@ -6,6 +6,7 @@ use sqlx::prelude::*; use serde::{Deserialize, Serialize}; use crate::core::protocol::CompleteDatabaseNameResponse; +use crate::core::protocol::request_validation::AuthorizationError; use crate::core::types::MySQLDatabase; use crate::core::types::MySQLUser; use crate::{ @@ -94,19 +95,19 @@ pub async fn create_databases( let mut results = BTreeMap::new(); for database_name in database_names { - if let Err(err) = validate_name(&database_name) { - results.insert( - database_name.to_owned(), - Err(CreateDatabaseError::SanitizationError(err)), - ); + if let Err(err) = validate_name(&database_name) + .map_err(AuthorizationError::SanitizationError) + .map_err(CreateDatabaseError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&database_name, unix_user) { - results.insert( - database_name.to_owned(), - Err(CreateDatabaseError::OwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(&database_name, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(CreateDatabaseError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } @@ -154,19 +155,19 @@ pub async fn drop_databases( let mut results = BTreeMap::new(); for database_name in database_names { - if let Err(err) = validate_name(&database_name) { - results.insert( - database_name.to_owned(), - Err(DropDatabaseError::SanitizationError(err)), - ); + if let Err(err) = validate_name(&database_name) + .map_err(AuthorizationError::SanitizationError) + .map_err(DropDatabaseError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&database_name, unix_user) { - results.insert( - database_name.to_owned(), - Err(DropDatabaseError::OwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(&database_name, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(DropDatabaseError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } @@ -257,19 +258,19 @@ pub async fn list_databases( let mut results = BTreeMap::new(); for database_name in database_names { - if let Err(err) = validate_name(&database_name) { - results.insert( - database_name.to_owned(), - Err(ListDatabasesError::SanitizationError(err)), - ); + if let Err(err) = validate_name(&database_name) + .map_err(AuthorizationError::SanitizationError) + .map_err(ListDatabasesError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&database_name, unix_user) { - results.insert( - database_name.to_owned(), - Err(ListDatabasesError::OwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(&database_name, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(ListDatabasesError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } diff --git a/src/server/sql/database_privilege_operations.rs b/src/server/sql/database_privilege_operations.rs index c50ea3f..f41dd74 100644 --- a/src/server/sql/database_privilege_operations.rs +++ b/src/server/sql/database_privilege_operations.rs @@ -31,14 +31,14 @@ use crate::{ DiffDoesNotApplyError, GetAllDatabasesPrivilegeDataError, GetDatabasesPrivilegeDataError, ListAllPrivilegesResponse, ListPrivilegesResponse, ModifyDatabasePrivilegesError, ModifyPrivilegesResponse, + request_validation::AuthorizationError, }, types::{MySQLDatabase, MySQLUser}, }, server::{ 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, - sql::user_operations::unsafe_user_exists, + sql::{database_operations::unsafe_database_exists, user_operations::unsafe_user_exists}, }, }; @@ -145,19 +145,19 @@ pub async fn get_databases_privilege_data( let mut results = BTreeMap::new(); for database_name in database_names.iter() { - if let Err(err) = validate_name(database_name) { - results.insert( - database_name.to_owned(), - Err(GetDatabasesPrivilegeDataError::SanitizationError(err)), - ); + if let Err(err) = validate_name(database_name) + .map_err(AuthorizationError::SanitizationError) + .map_err(GetDatabasesPrivilegeDataError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(database_name, unix_user) { - results.insert( - database_name.to_owned(), - Err(GetDatabasesPrivilegeDataError::OwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(database_name, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(GetDatabasesPrivilegeDataError::AuthorizationError) + { + results.insert(database_name.to_owned(), Err(err)); continue; } @@ -411,37 +411,35 @@ pub async fn apply_privilege_diffs( diff.get_database_name().to_owned(), diff.get_user_name().to_owned(), ); - if let Err(err) = validate_name(diff.get_database_name()) { - results.insert( - key, - Err(ModifyDatabasePrivilegesError::DatabaseSanitizationError( - err, - )), - ); + if let Err(err) = validate_name(diff.get_database_name()) + .map_err(AuthorizationError::SanitizationError) + .map_err(ModifyDatabasePrivilegesError::DatabaseAuthorizationError) + { + results.insert(key, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(diff.get_database_name(), unix_user) { - results.insert( - key, - Err(ModifyDatabasePrivilegesError::DatabaseOwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(diff.get_database_name(), unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(ModifyDatabasePrivilegesError::DatabaseAuthorizationError) + { + results.insert(key, Err(err)); continue; } - if let Err(err) = validate_name(diff.get_user_name()) { - results.insert( - key, - Err(ModifyDatabasePrivilegesError::UserSanitizationError(err)), - ); + if let Err(err) = validate_name(diff.get_user_name()) + .map_err(AuthorizationError::SanitizationError) + .map_err(ModifyDatabasePrivilegesError::UserAuthorizationError) + { + results.insert(key, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(diff.get_user_name(), unix_user) { - results.insert( - key, - Err(ModifyDatabasePrivilegesError::UserOwnershipError(err)), - ); + if let Err(err) = validate_ownership_by_unix_user(diff.get_user_name(), unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(ModifyDatabasePrivilegesError::UserAuthorizationError) + { + results.insert(key, Err(err)); continue; } diff --git a/src/server/sql/user_operations.rs b/src/server/sql/user_operations.rs index afe728b..bdf5c21 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::MySqlConnection; use sqlx::prelude::*; +use crate::core::protocol::request_validation::AuthorizationError; use crate::{ core::{ common::UnixUser, @@ -99,13 +100,19 @@ pub async fn create_database_users( let mut results = BTreeMap::new(); for db_user in db_users { - if let Err(err) = validate_name(&db_user) { - results.insert(db_user, Err(CreateUserError::SanitizationError(err))); + if let Err(err) = validate_name(&db_user) + .map_err(AuthorizationError::SanitizationError) + .map_err(CreateUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) { - results.insert(db_user, Err(CreateUserError::OwnershipError(err))); + if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(CreateUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } @@ -146,13 +153,19 @@ pub async fn drop_database_users( let mut results = BTreeMap::new(); for db_user in db_users { - if let Err(err) = validate_name(&db_user) { - results.insert(db_user, Err(DropUserError::SanitizationError(err))); + if let Err(err) = validate_name(&db_user) + .map_err(AuthorizationError::SanitizationError) + .map_err(DropUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) { - results.insert(db_user, Err(DropUserError::OwnershipError(err))); + if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(DropUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } @@ -191,13 +204,13 @@ pub async fn set_password_for_database_user( connection: &mut MySqlConnection, _db_is_mariadb: bool, ) -> SetUserPasswordResponse { - if let Err(err) = validate_name(db_user) { - return Err(SetPasswordError::SanitizationError(err)); - } + validate_name(db_user) + .map_err(AuthorizationError::SanitizationError) + .map_err(SetPasswordError::AuthorizationError)?; - if let Err(err) = validate_ownership_by_unix_user(db_user, unix_user) { - return Err(SetPasswordError::OwnershipError(err)); - } + validate_ownership_by_unix_user(db_user, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(SetPasswordError::AuthorizationError)?; match unsafe_user_exists(db_user, &mut *connection).await { Ok(false) => return Err(SetPasswordError::UserDoesNotExist), @@ -282,13 +295,19 @@ pub async fn lock_database_users( let mut results = BTreeMap::new(); for db_user in db_users { - if let Err(err) = validate_name(&db_user) { - results.insert(db_user, Err(LockUserError::SanitizationError(err))); + if let Err(err) = validate_name(&db_user) + .map_err(AuthorizationError::SanitizationError) + .map_err(LockUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) { - results.insert(db_user, Err(LockUserError::OwnershipError(err))); + if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(LockUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } @@ -343,13 +362,19 @@ pub async fn unlock_database_users( let mut results = BTreeMap::new(); for db_user in db_users { - if let Err(err) = validate_name(&db_user) { - results.insert(db_user, Err(UnlockUserError::SanitizationError(err))); + if let Err(err) = validate_name(&db_user) + .map_err(AuthorizationError::SanitizationError) + .map_err(UnlockUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) { - results.insert(db_user, Err(UnlockUserError::OwnershipError(err))); + if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(UnlockUserError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } @@ -452,13 +477,19 @@ pub async fn list_database_users( let mut results = BTreeMap::new(); for db_user in db_users { - if let Err(err) = validate_name(&db_user) { - results.insert(db_user, Err(ListUsersError::SanitizationError(err))); + if let Err(err) = validate_name(&db_user) + .map_err(AuthorizationError::SanitizationError) + .map_err(ListUsersError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; } - if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) { - results.insert(db_user, Err(ListUsersError::OwnershipError(err))); + if let Err(err) = validate_ownership_by_unix_user(&db_user, unix_user) + .map_err(AuthorizationError::OwnershipError) + .map_err(ListUsersError::AuthorizationError) + { + results.insert(db_user, Err(err)); continue; }