core/protocol: split commands into separate files
All checks were successful
All checks were successful
This commit is contained in:
@@ -70,7 +70,7 @@ pub async fn create_users(
|
|||||||
.interact()?
|
.interact()?
|
||||||
{
|
{
|
||||||
let password = read_password_from_stdin_with_double_check(username)?;
|
let password = read_password_from_stdin_with_double_check(username)?;
|
||||||
let message = Request::PasswdUser(username.to_owned(), password);
|
let message = Request::PasswdUser((username.to_owned(), password));
|
||||||
|
|
||||||
if let Err(err) = server_connection.send(message).await {
|
if let Err(err) = server_connection.send(message).await {
|
||||||
server_connection.close().await.ok();
|
server_connection.close().await.ok();
|
||||||
@@ -78,7 +78,7 @@ pub async fn create_users(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match server_connection.next().await {
|
match server_connection.next().await {
|
||||||
Some(Ok(Response::PasswdUser(result))) => {
|
Some(Ok(Response::SetUserPassword(result))) => {
|
||||||
print_set_password_output_status(&result, username)
|
print_set_password_output_status(&result, username)
|
||||||
}
|
}
|
||||||
response => return erroneous_server_response(response),
|
response => return erroneous_server_response(response),
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ pub async fn passwd_user(
|
|||||||
read_password_from_stdin_with_double_check(&args.username)?
|
read_password_from_stdin_with_double_check(&args.username)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let message = Request::PasswdUser(args.username.to_owned(), password);
|
let message = Request::PasswdUser((args.username.to_owned(), password));
|
||||||
|
|
||||||
if let Err(err) = server_connection.send(message).await {
|
if let Err(err) = server_connection.send(message).await {
|
||||||
server_connection.close().await.ok();
|
server_connection.close().await.ok();
|
||||||
@@ -81,7 +81,7 @@ pub async fn passwd_user(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let result = match server_connection.next().await {
|
let result = match server_connection.next().await {
|
||||||
Some(Ok(Response::PasswdUser(result))) => result,
|
Some(Ok(Response::SetUserPassword(result))) => result,
|
||||||
response => return erroneous_server_response(response),
|
response => return erroneous_server_response(response),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::core::protocol::{
|
use crate::core::protocol::{
|
||||||
CreateDatabaseError, CreateUserError, DbOrUser, DropDatabaseError, DropUserError,
|
CreateDatabaseError, CreateUserError, DropDatabaseError, DropUserError,
|
||||||
GetDatabasesPrivilegeDataError, ListUsersError,
|
GetDatabasesPrivilegeDataError, ListUsersError, request_validation::DbOrUser,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn name_validation_error_to_error_message(name: &str, db_or_user: DbOrUser) -> String {
|
pub fn name_validation_error_to_error_message(name: &str, db_or_user: DbOrUser) -> String {
|
||||||
|
|||||||
@@ -235,10 +235,10 @@ async fn passwd_users(
|
|||||||
|
|
||||||
for user in users {
|
for user in users {
|
||||||
let password = read_password_from_stdin_with_double_check(&user.user)?;
|
let password = read_password_from_stdin_with_double_check(&user.user)?;
|
||||||
let message = Request::PasswdUser(user.user.to_owned(), password);
|
let message = Request::PasswdUser((user.user.to_owned(), password));
|
||||||
server_connection.send(message).await?;
|
server_connection.send(message).await?;
|
||||||
match server_connection.next().await {
|
match server_connection.next().await {
|
||||||
Some(Ok(Response::PasswdUser(result))) => match result {
|
Some(Ok(Response::SetUserPassword(result))) => match result {
|
||||||
Ok(()) => println!("Password updated for user '{}'.", &user.user),
|
Ok(()) => println!("Password updated for user '{}'.", &user.user),
|
||||||
Err(_) => eprintln!(
|
Err(_) => eprintln!(
|
||||||
"{}: Failed to update password for user '{}'.",
|
"{}: Failed to update password for user '{}'.",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pub mod request_response;
|
mod commands;
|
||||||
pub mod server_responses;
|
pub mod request_validation;
|
||||||
|
|
||||||
pub use request_response::*;
|
pub use commands::*;
|
||||||
pub use server_responses::*;
|
|
||||||
|
|||||||
105
src/core/protocol/commands.rs
Normal file
105
src/core/protocol/commands.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
mod create_databases;
|
||||||
|
mod create_users;
|
||||||
|
mod drop_databases;
|
||||||
|
mod drop_users;
|
||||||
|
mod list_all_databases;
|
||||||
|
mod list_all_privileges;
|
||||||
|
mod list_all_users;
|
||||||
|
mod list_databases;
|
||||||
|
mod list_privileges;
|
||||||
|
mod list_users;
|
||||||
|
mod lock_users;
|
||||||
|
mod modify_privileges;
|
||||||
|
mod passwd_user;
|
||||||
|
mod unlock_users;
|
||||||
|
|
||||||
|
pub use create_databases::*;
|
||||||
|
pub use create_users::*;
|
||||||
|
pub use drop_databases::*;
|
||||||
|
pub use drop_users::*;
|
||||||
|
pub use list_all_databases::*;
|
||||||
|
pub use list_all_privileges::*;
|
||||||
|
pub use list_all_users::*;
|
||||||
|
pub use list_databases::*;
|
||||||
|
pub use list_privileges::*;
|
||||||
|
pub use list_users::*;
|
||||||
|
pub use lock_users::*;
|
||||||
|
pub use modify_privileges::*;
|
||||||
|
pub use passwd_user::*;
|
||||||
|
pub use unlock_users::*;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::net::UnixStream;
|
||||||
|
use tokio_serde::{Framed as SerdeFramed, formats::Bincode};
|
||||||
|
use tokio_util::codec::{Framed, LengthDelimitedCodec};
|
||||||
|
|
||||||
|
pub type ServerToClientMessageStream = SerdeFramed<
|
||||||
|
Framed<UnixStream, LengthDelimitedCodec>,
|
||||||
|
Request,
|
||||||
|
Response,
|
||||||
|
Bincode<Request, Response>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub type ClientToServerMessageStream = SerdeFramed<
|
||||||
|
Framed<UnixStream, LengthDelimitedCodec>,
|
||||||
|
Response,
|
||||||
|
Request,
|
||||||
|
Bincode<Response, Request>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub fn create_server_to_client_message_stream(socket: UnixStream) -> ServerToClientMessageStream {
|
||||||
|
let length_delimited = Framed::new(socket, LengthDelimitedCodec::new());
|
||||||
|
tokio_serde::Framed::new(length_delimited, Bincode::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_client_to_server_message_stream(socket: UnixStream) -> ClientToServerMessageStream {
|
||||||
|
let length_delimited = Framed::new(socket, LengthDelimitedCodec::new());
|
||||||
|
tokio_serde::Framed::new(length_delimited, Bincode::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Request {
|
||||||
|
CreateDatabases(CreateDatabasesRequest),
|
||||||
|
DropDatabases(DropDatabasesRequest),
|
||||||
|
ListDatabases(ListDatabasesRequest),
|
||||||
|
ListPrivileges(ListPrivilegesRequest),
|
||||||
|
ModifyPrivileges(ModifyPrivilegesRequest),
|
||||||
|
|
||||||
|
CreateUsers(CreateUsersRequest),
|
||||||
|
DropUsers(DropUsersRequest),
|
||||||
|
PasswdUser(SetUserPasswordRequest),
|
||||||
|
ListUsers(ListUsersRequest),
|
||||||
|
LockUsers(LockUsersRequest),
|
||||||
|
UnlockUsers(UnlockUsersRequest),
|
||||||
|
|
||||||
|
// Commit,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: include a generic "message" that will display a message to the user?
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Response {
|
||||||
|
// Specific data for specific commands
|
||||||
|
CreateDatabases(CreateDatabasesResponse),
|
||||||
|
DropDatabases(DropDatabasesResponse),
|
||||||
|
ListDatabases(ListDatabasesResponse),
|
||||||
|
ListAllDatabases(ListAllDatabasesResponse),
|
||||||
|
ListPrivileges(ListPrivilegesResponse),
|
||||||
|
ListAllPrivileges(ListAllPrivilegesResponse),
|
||||||
|
ModifyPrivileges(ModifyPrivilegesResponse),
|
||||||
|
|
||||||
|
CreateUsers(CreateUsersResponse),
|
||||||
|
DropUsers(DropUsersResponse),
|
||||||
|
SetUserPassword(SetUserPasswordResponse),
|
||||||
|
ListUsers(ListUsersResponse),
|
||||||
|
ListAllUsers(ListAllUsersResponse),
|
||||||
|
LockUsers(LockUsersResponse),
|
||||||
|
UnlockUsers(UnlockUsersResponse),
|
||||||
|
|
||||||
|
// Generic responses
|
||||||
|
Ready,
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
76
src/core/protocol/commands/create_databases.rs
Normal file
76
src/core/protocol/commands/create_databases.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLDatabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type CreateDatabasesRequest = Vec<MySQLDatabase>;
|
||||||
|
|
||||||
|
pub type CreateDatabasesResponse = BTreeMap<MySQLDatabase, Result<(), CreateDatabaseError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum CreateDatabaseError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
DatabaseAlreadyExists,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_create_databases_output_status(output: &CreateDatabasesResponse) {
|
||||||
|
for (database_name, result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("Database '{}' created successfully.", database_name);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(database_name));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_create_databases_output_status_json(output: &CreateDatabasesResponse) {
|
||||||
|
let value = output
|
||||||
|
.iter()
|
||||||
|
.map(|(name, result)| match result {
|
||||||
|
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||||
|
Err(err) => (
|
||||||
|
name.to_string(),
|
||||||
|
json!({
|
||||||
|
"status": "error",
|
||||||
|
"error": err.to_error_message(name),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Map<_, _>>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&value)
|
||||||
|
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateDatabaseError {
|
||||||
|
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||||
|
match self {
|
||||||
|
CreateDatabaseError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
CreateDatabaseError::OwnershipError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
CreateDatabaseError::DatabaseAlreadyExists => {
|
||||||
|
format!("Database {} already exists.", database_name)
|
||||||
|
}
|
||||||
|
CreateDatabaseError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/core/protocol/commands/create_users.rs
Normal file
74
src/core/protocol/commands/create_users.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type CreateUsersRequest = Vec<MySQLUser>;
|
||||||
|
|
||||||
|
pub type CreateUsersResponse = BTreeMap<MySQLUser, Result<(), CreateUserError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum CreateUserError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
UserAlreadyExists,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_create_users_output_status(output: &CreateUsersResponse) {
|
||||||
|
for (username, result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("User '{}' created successfully.", username);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(username));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_create_users_output_status_json(output: &CreateUsersResponse) {
|
||||||
|
let value = output
|
||||||
|
.iter()
|
||||||
|
.map(|(name, result)| match result {
|
||||||
|
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||||
|
Err(err) => (
|
||||||
|
name.to_string(),
|
||||||
|
json!({
|
||||||
|
"status": "error",
|
||||||
|
"error": err.to_error_message(name),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Map<_, _>>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&value)
|
||||||
|
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateUserError {
|
||||||
|
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
CreateUserError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(username, DbOrUser::User)
|
||||||
|
}
|
||||||
|
CreateUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
CreateUserError::UserAlreadyExists => {
|
||||||
|
format!("User '{}' already exists.", username)
|
||||||
|
}
|
||||||
|
CreateUserError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/core/protocol/commands/drop_databases.rs
Normal file
79
src/core/protocol/commands/drop_databases.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLDatabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type DropDatabasesRequest = Vec<MySQLDatabase>;
|
||||||
|
|
||||||
|
pub type DropDatabasesResponse = BTreeMap<MySQLDatabase, Result<(), DropDatabaseError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum DropDatabaseError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
DatabaseDoesNotExist,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_drop_databases_output_status(output: &DropDatabasesResponse) {
|
||||||
|
for (database_name, result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!(
|
||||||
|
"Database '{}' dropped successfully.",
|
||||||
|
database_name.as_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(database_name));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_drop_databases_output_status_json(output: &DropDatabasesResponse) {
|
||||||
|
let value = output
|
||||||
|
.iter()
|
||||||
|
.map(|(name, result)| match result {
|
||||||
|
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||||
|
Err(err) => (
|
||||||
|
name.to_string(),
|
||||||
|
json!({
|
||||||
|
"status": "error",
|
||||||
|
"error": err.to_error_message(name),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Map<_, _>>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&value)
|
||||||
|
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DropDatabaseError {
|
||||||
|
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||||
|
match self {
|
||||||
|
DropDatabaseError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
DropDatabaseError::OwnershipError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
DropDatabaseError::DatabaseDoesNotExist => {
|
||||||
|
format!("Database {} does not exist.", database_name)
|
||||||
|
}
|
||||||
|
DropDatabaseError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/core/protocol/commands/drop_users.rs
Normal file
72
src/core/protocol/commands/drop_users.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type DropUsersRequest = Vec<MySQLUser>;
|
||||||
|
|
||||||
|
pub type DropUsersResponse = BTreeMap<MySQLUser, Result<(), DropUserError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum DropUserError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
UserDoesNotExist,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_drop_users_output_status(output: &DropUsersResponse) {
|
||||||
|
for (username, result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("User '{}' dropped successfully.", username);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(username));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_drop_users_output_status_json(output: &DropUsersResponse) {
|
||||||
|
let value = output
|
||||||
|
.iter()
|
||||||
|
.map(|(name, result)| match result {
|
||||||
|
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||||
|
Err(err) => (
|
||||||
|
name.to_string(),
|
||||||
|
json!({
|
||||||
|
"status": "error",
|
||||||
|
"error": err.to_error_message(name),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Map<_, _>>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&value)
|
||||||
|
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DropUserError {
|
||||||
|
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
DropUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
DropUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
DropUserError::UserDoesNotExist => {
|
||||||
|
format!("User '{}' does not exist.", username)
|
||||||
|
}
|
||||||
|
DropUserError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/core/protocol/commands/list_all_databases.rs
Normal file
18
src/core/protocol/commands/list_all_databases.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::server::sql::database_operations::DatabaseRow;
|
||||||
|
|
||||||
|
pub type ListAllDatabasesResponse = Result<Vec<DatabaseRow>, ListAllDatabasesError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ListAllDatabasesError {
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListAllDatabasesError {
|
||||||
|
pub fn to_error_message(&self) -> String {
|
||||||
|
match self {
|
||||||
|
ListAllDatabasesError::MySqlError(err) => format!("MySQL error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/core/protocol/commands/list_all_privileges.rs
Normal file
19
src/core/protocol/commands/list_all_privileges.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::core::database_privileges::DatabasePrivilegeRow;
|
||||||
|
|
||||||
|
pub type ListAllPrivilegesResponse =
|
||||||
|
Result<Vec<DatabasePrivilegeRow>, GetAllDatabasesPrivilegeDataError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum GetAllDatabasesPrivilegeDataError {
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetAllDatabasesPrivilegeDataError {
|
||||||
|
pub fn to_error_message(&self) -> String {
|
||||||
|
match self {
|
||||||
|
GetAllDatabasesPrivilegeDataError::MySqlError(err) => format!("MySQL error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/core/protocol/commands/list_all_users.rs
Normal file
18
src/core/protocol/commands/list_all_users.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::server::sql::user_operations::DatabaseUser;
|
||||||
|
|
||||||
|
pub type ListAllUsersResponse = Result<Vec<DatabaseUser>, ListAllUsersError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ListAllUsersError {
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListAllUsersError {
|
||||||
|
pub fn to_error_message(&self) -> String {
|
||||||
|
match self {
|
||||||
|
ListAllUsersError::MySqlError(err) => format!("MySQL error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/core/protocol/commands/list_databases.rs
Normal file
42
src/core/protocol/commands/list_databases.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLDatabase,
|
||||||
|
},
|
||||||
|
server::sql::database_operations::DatabaseRow,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type ListDatabasesRequest = Option<Vec<MySQLDatabase>>;
|
||||||
|
|
||||||
|
pub type ListDatabasesResponse = BTreeMap<MySQLDatabase, Result<DatabaseRow, ListDatabasesError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ListDatabasesError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
DatabaseDoesNotExist,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListDatabasesError {
|
||||||
|
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||||
|
match self {
|
||||||
|
ListDatabasesError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
ListDatabasesError::OwnershipError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
ListDatabasesError::DatabaseDoesNotExist => {
|
||||||
|
format!("Database '{}' does not exist.", database_name)
|
||||||
|
}
|
||||||
|
ListDatabasesError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/core/protocol/commands/list_privileges.rs
Normal file
45
src/core/protocol/commands/list_privileges.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// TODO: merge all rows into a single collection.
|
||||||
|
// they already contain which database they belong to.
|
||||||
|
// no need to index by database name.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
database_privileges::DatabasePrivilegeRow,
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLDatabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type ListPrivilegesRequest = Option<Vec<MySQLDatabase>>;
|
||||||
|
|
||||||
|
pub type ListPrivilegesResponse =
|
||||||
|
BTreeMap<MySQLDatabase, Result<Vec<DatabasePrivilegeRow>, GetDatabasesPrivilegeDataError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum GetDatabasesPrivilegeDataError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
DatabaseDoesNotExist,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetDatabasesPrivilegeDataError {
|
||||||
|
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||||
|
match self {
|
||||||
|
GetDatabasesPrivilegeDataError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
GetDatabasesPrivilegeDataError::OwnershipError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
GetDatabasesPrivilegeDataError::DatabaseDoesNotExist => {
|
||||||
|
format!("Database '{}' does not exist.", database_name)
|
||||||
|
}
|
||||||
|
GetDatabasesPrivilegeDataError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/core/protocol/commands/list_users.rs
Normal file
40
src/core/protocol/commands/list_users.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLUser,
|
||||||
|
},
|
||||||
|
server::sql::user_operations::DatabaseUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type ListUsersRequest = Option<Vec<MySQLUser>>;
|
||||||
|
|
||||||
|
pub type ListUsersResponse = BTreeMap<MySQLUser, Result<DatabaseUser, ListUsersError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ListUsersError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
UserDoesNotExist,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListUsersError {
|
||||||
|
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
ListUsersError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(username, DbOrUser::User)
|
||||||
|
}
|
||||||
|
ListUsersError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
ListUsersError::UserDoesNotExist => {
|
||||||
|
format!("User '{}' does not exist.", username)
|
||||||
|
}
|
||||||
|
ListUsersError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/core/protocol/commands/lock_users.rs
Normal file
76
src/core/protocol/commands/lock_users.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type LockUsersRequest = Vec<MySQLUser>;
|
||||||
|
|
||||||
|
pub type LockUsersResponse = BTreeMap<MySQLUser, Result<(), LockUserError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum LockUserError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
UserDoesNotExist,
|
||||||
|
UserIsAlreadyLocked,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_lock_users_output_status(output: &LockUsersResponse) {
|
||||||
|
for (username, result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("User '{}' locked successfully.", username);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(username));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_lock_users_output_status_json(output: &LockUsersResponse) {
|
||||||
|
let value = output
|
||||||
|
.iter()
|
||||||
|
.map(|(name, result)| match result {
|
||||||
|
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||||
|
Err(err) => (
|
||||||
|
name.to_string(),
|
||||||
|
json!({
|
||||||
|
"status": "error",
|
||||||
|
"error": err.to_error_message(name),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Map<_, _>>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&value)
|
||||||
|
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockUserError {
|
||||||
|
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
LockUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
LockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
LockUserError::UserDoesNotExist => {
|
||||||
|
format!("User '{}' does not exist.", username)
|
||||||
|
}
|
||||||
|
LockUserError::UserIsAlreadyLocked => {
|
||||||
|
format!("User '{}' is already locked.", username)
|
||||||
|
}
|
||||||
|
LockUserError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/core/protocol/commands/modify_privileges.rs
Normal file
107
src/core/protocol/commands/modify_privileges.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
database_privileges::{DatabasePrivilegeRow, DatabasePrivilegeRowDiff, DatabasePrivilegesDiff},
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::{MySQLDatabase, MySQLUser},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type ModifyPrivilegesRequest = BTreeSet<DatabasePrivilegesDiff>;
|
||||||
|
|
||||||
|
pub type ModifyPrivilegesResponse =
|
||||||
|
BTreeMap<(MySQLDatabase, MySQLUser), Result<(), ModifyDatabasePrivilegesError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ModifyDatabasePrivilegesError {
|
||||||
|
DatabaseSanitizationError(NameValidationError),
|
||||||
|
DatabaseOwnershipError(OwnerValidationError),
|
||||||
|
UserSanitizationError(NameValidationError),
|
||||||
|
UserOwnershipError(OwnerValidationError),
|
||||||
|
DatabaseDoesNotExist,
|
||||||
|
DiffDoesNotApply(DiffDoesNotApplyError),
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum DiffDoesNotApplyError {
|
||||||
|
RowAlreadyExists(MySQLDatabase, MySQLUser),
|
||||||
|
RowDoesNotExist(MySQLDatabase, MySQLUser),
|
||||||
|
RowPrivilegeChangeDoesNotApply(DatabasePrivilegeRowDiff, DatabasePrivilegeRow),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_modify_database_privileges_output_status(output: &ModifyPrivilegesResponse) {
|
||||||
|
for ((database_name, username), result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!(
|
||||||
|
"Privileges for user '{}' on database '{}' modified successfully.",
|
||||||
|
username, database_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(database_name, username));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifyDatabasePrivilegesError {
|
||||||
|
pub fn to_error_message(&self, database_name: &MySQLDatabase, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
ModifyDatabasePrivilegesError::DatabaseSanitizationError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
ModifyDatabasePrivilegesError::DatabaseOwnershipError(err) => {
|
||||||
|
err.to_error_message(database_name, DbOrUser::Database)
|
||||||
|
}
|
||||||
|
ModifyDatabasePrivilegesError::UserSanitizationError(err) => {
|
||||||
|
err.to_error_message(username, DbOrUser::User)
|
||||||
|
}
|
||||||
|
ModifyDatabasePrivilegesError::UserOwnershipError(err) => {
|
||||||
|
err.to_error_message(username, DbOrUser::User)
|
||||||
|
}
|
||||||
|
ModifyDatabasePrivilegesError::DatabaseDoesNotExist => {
|
||||||
|
format!("Database '{}' does not exist.", database_name)
|
||||||
|
}
|
||||||
|
ModifyDatabasePrivilegesError::DiffDoesNotApply(diff) => {
|
||||||
|
format!(
|
||||||
|
"Could not apply privilege change:\n{}",
|
||||||
|
diff.to_error_message()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ModifyDatabasePrivilegesError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiffDoesNotApplyError {
|
||||||
|
pub fn to_error_message(&self) -> String {
|
||||||
|
match self {
|
||||||
|
DiffDoesNotApplyError::RowAlreadyExists(database_name, username) => {
|
||||||
|
format!(
|
||||||
|
"Privileges for user '{}' on database '{}' already exist.",
|
||||||
|
username, database_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DiffDoesNotApplyError::RowDoesNotExist(database_name, username) => {
|
||||||
|
format!(
|
||||||
|
"Privileges for user '{}' on database '{}' do not exist.",
|
||||||
|
username, database_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DiffDoesNotApplyError::RowPrivilegeChangeDoesNotApply(diff, row) => {
|
||||||
|
format!(
|
||||||
|
"Could not apply privilege change {:?} to row {:?}",
|
||||||
|
diff, row
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/core/protocol/commands/passwd_user.rs
Normal file
47
src/core/protocol/commands/passwd_user.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type SetUserPasswordRequest = (MySQLUser, String);
|
||||||
|
|
||||||
|
pub type SetUserPasswordResponse = Result<(), SetPasswordError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum SetPasswordError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
UserDoesNotExist,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_set_password_output_status(output: &SetUserPasswordResponse, username: &MySQLUser) {
|
||||||
|
match output {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("Password for user '{}' set successfully.", username);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(username));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SetPasswordError {
|
||||||
|
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
SetPasswordError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(username, DbOrUser::User)
|
||||||
|
}
|
||||||
|
SetPasswordError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
SetPasswordError::UserDoesNotExist => {
|
||||||
|
format!("User '{}' does not exist.", username)
|
||||||
|
}
|
||||||
|
SetPasswordError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/core/protocol/commands/unlock_users.rs
Normal file
78
src/core/protocol/commands/unlock_users.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
protocol::request_validation::{DbOrUser, NameValidationError, OwnerValidationError},
|
||||||
|
types::MySQLUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type UnlockUsersRequest = Vec<MySQLUser>;
|
||||||
|
|
||||||
|
pub type UnlockUsersResponse = BTreeMap<MySQLUser, Result<(), UnlockUserError>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum UnlockUserError {
|
||||||
|
SanitizationError(NameValidationError),
|
||||||
|
OwnershipError(OwnerValidationError),
|
||||||
|
UserDoesNotExist,
|
||||||
|
UserIsAlreadyUnlocked,
|
||||||
|
MySqlError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_unlock_users_output_status(output: &UnlockUsersResponse) {
|
||||||
|
for (username, result) in output {
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("User '{}' unlocked successfully.", username);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err.to_error_message(username));
|
||||||
|
println!("Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_unlock_users_output_status_json(output: &UnlockUsersResponse) {
|
||||||
|
let value = output
|
||||||
|
.iter()
|
||||||
|
.map(|(name, result)| match result {
|
||||||
|
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||||
|
Err(err) => (
|
||||||
|
name.to_string(),
|
||||||
|
json!({
|
||||||
|
"status": "error",
|
||||||
|
"error": err.to_error_message(name),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<serde_json::Map<_, _>>();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&value)
|
||||||
|
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnlockUserError {
|
||||||
|
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||||
|
match self {
|
||||||
|
UnlockUserError::SanitizationError(err) => {
|
||||||
|
err.to_error_message(username, DbOrUser::User)
|
||||||
|
}
|
||||||
|
UnlockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||||
|
UnlockUserError::UserDoesNotExist => {
|
||||||
|
format!("User '{}' does not exist.", username)
|
||||||
|
}
|
||||||
|
UnlockUserError::UserIsAlreadyUnlocked => {
|
||||||
|
format!("User '{}' is already unlocked.", username)
|
||||||
|
}
|
||||||
|
UnlockUserError::MySqlError(err) => {
|
||||||
|
format!("MySQL error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio::net::UnixStream;
|
|
||||||
use tokio_serde::{Framed as SerdeFramed, formats::Bincode};
|
|
||||||
use tokio_util::codec::{Framed, LengthDelimitedCodec};
|
|
||||||
|
|
||||||
use crate::core::{
|
|
||||||
database_privileges::DatabasePrivilegesDiff,
|
|
||||||
protocol::*,
|
|
||||||
types::{MySQLDatabase, MySQLUser},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type ServerToClientMessageStream = SerdeFramed<
|
|
||||||
Framed<UnixStream, LengthDelimitedCodec>,
|
|
||||||
Request,
|
|
||||||
Response,
|
|
||||||
Bincode<Request, Response>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
pub type ClientToServerMessageStream = SerdeFramed<
|
|
||||||
Framed<UnixStream, LengthDelimitedCodec>,
|
|
||||||
Response,
|
|
||||||
Request,
|
|
||||||
Bincode<Response, Request>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
pub fn create_server_to_client_message_stream(socket: UnixStream) -> ServerToClientMessageStream {
|
|
||||||
let length_delimited = Framed::new(socket, LengthDelimitedCodec::new());
|
|
||||||
tokio_serde::Framed::new(length_delimited, Bincode::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_client_to_server_message_stream(socket: UnixStream) -> ClientToServerMessageStream {
|
|
||||||
let length_delimited = Framed::new(socket, LengthDelimitedCodec::new());
|
|
||||||
tokio_serde::Framed::new(length_delimited, Bincode::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Request {
|
|
||||||
CreateDatabases(Vec<MySQLDatabase>),
|
|
||||||
DropDatabases(Vec<MySQLDatabase>),
|
|
||||||
ListDatabases(Option<Vec<MySQLDatabase>>),
|
|
||||||
ListPrivileges(Option<Vec<MySQLDatabase>>),
|
|
||||||
ModifyPrivileges(BTreeSet<DatabasePrivilegesDiff>),
|
|
||||||
|
|
||||||
CreateUsers(Vec<MySQLUser>),
|
|
||||||
DropUsers(Vec<MySQLUser>),
|
|
||||||
PasswdUser(MySQLUser, String),
|
|
||||||
ListUsers(Option<Vec<MySQLUser>>),
|
|
||||||
LockUsers(Vec<MySQLUser>),
|
|
||||||
UnlockUsers(Vec<MySQLUser>),
|
|
||||||
|
|
||||||
// Commit,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: include a generic "message" that will display a message to the user?
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Response {
|
|
||||||
// Specific data for specific commands
|
|
||||||
CreateDatabases(CreateDatabasesOutput),
|
|
||||||
DropDatabases(DropDatabasesOutput),
|
|
||||||
ListDatabases(ListDatabasesOutput),
|
|
||||||
ListAllDatabases(ListAllDatabasesOutput),
|
|
||||||
ListPrivileges(GetDatabasesPrivilegeData),
|
|
||||||
ListAllPrivileges(GetAllDatabasesPrivilegeData),
|
|
||||||
ModifyPrivileges(ModifyDatabasePrivilegesOutput),
|
|
||||||
|
|
||||||
CreateUsers(CreateUsersOutput),
|
|
||||||
DropUsers(DropUsersOutput),
|
|
||||||
PasswdUser(SetPasswordOutput),
|
|
||||||
ListUsers(ListUsersOutput),
|
|
||||||
ListAllUsers(ListAllUsersOutput),
|
|
||||||
LockUsers(LockUsersOutput),
|
|
||||||
UnlockUsers(UnlockUsersOutput),
|
|
||||||
|
|
||||||
// Generic responses
|
|
||||||
Ready,
|
|
||||||
Error(String),
|
|
||||||
}
|
|
||||||
117
src/core/protocol/request_validation.rs
Normal file
117
src/core/protocol/request_validation.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
use indoc::indoc;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::core::common::UnixUser;
|
||||||
|
|
||||||
|
/// This enum is used to differentiate between database and user operations.
|
||||||
|
/// Their output are very similar, but there are slight differences in the words used.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DbOrUser {
|
||||||
|
Database,
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbOrUser {
|
||||||
|
pub fn lowercased(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
DbOrUser::Database => "database",
|
||||||
|
DbOrUser::User => "user",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capitalized(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
DbOrUser::Database => "Database",
|
||||||
|
DbOrUser::User => "User",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub enum NameValidationError {
|
||||||
|
EmptyString,
|
||||||
|
InvalidCharacters,
|
||||||
|
TooLong,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NameValidationError {
|
||||||
|
pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
|
||||||
|
match self {
|
||||||
|
NameValidationError::EmptyString => {
|
||||||
|
format!("{} name cannot be empty.", db_or_user.capitalized()).to_owned()
|
||||||
|
}
|
||||||
|
NameValidationError::TooLong => format!(
|
||||||
|
"{} is too long. Maximum length is 64 characters.",
|
||||||
|
db_or_user.capitalized()
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
NameValidationError::InvalidCharacters => format!(
|
||||||
|
indoc! {r#"
|
||||||
|
Invalid characters in {} name: '{}'
|
||||||
|
|
||||||
|
Only A-Z, a-z, 0-9, _ (underscore) and - (dash) are permitted.
|
||||||
|
"#},
|
||||||
|
db_or_user.lowercased(),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnerValidationError {
|
||||||
|
pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
|
||||||
|
let user = UnixUser::from_enviroment();
|
||||||
|
|
||||||
|
let UnixUser {
|
||||||
|
username,
|
||||||
|
mut groups,
|
||||||
|
} = user.unwrap_or(UnixUser {
|
||||||
|
username: "???".to_string(),
|
||||||
|
groups: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
groups.sort();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
OwnerValidationError::NoMatch => format!(
|
||||||
|
indoc! {r#"
|
||||||
|
Invalid {} name prefix: '{}' does not match your username or any of your groups.
|
||||||
|
Are you sure you are allowed to create {} names with this prefix?
|
||||||
|
The format should be: <prefix>_<{} name>
|
||||||
|
|
||||||
|
Allowed prefixes:
|
||||||
|
- {}
|
||||||
|
{}
|
||||||
|
"#},
|
||||||
|
db_or_user.lowercased(),
|
||||||
|
name,
|
||||||
|
db_or_user.lowercased(),
|
||||||
|
db_or_user.lowercased(),
|
||||||
|
username,
|
||||||
|
groups
|
||||||
|
.into_iter()
|
||||||
|
.filter(|g| g != &username)
|
||||||
|
.map(|g| format!(" - {}", g))
|
||||||
|
.join("\n"),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
OwnerValidationError::StringEmpty => format!(
|
||||||
|
"'{}' is not a valid {} name.",
|
||||||
|
name,
|
||||||
|
db_or_user.lowercased()
|
||||||
|
)
|
||||||
|
.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
|
||||||
|
// The name is empty, which is invalid
|
||||||
|
StringEmpty,
|
||||||
|
}
|
||||||
@@ -1,773 +0,0 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use indoc::indoc;
|
|
||||||
use itertools::Itertools;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
core::{
|
|
||||||
common::UnixUser,
|
|
||||||
database_privileges::{DatabasePrivilegeRow, DatabasePrivilegeRowDiff},
|
|
||||||
types::{MySQLDatabase, MySQLUser},
|
|
||||||
},
|
|
||||||
server::sql::{database_operations::DatabaseRow, user_operations::DatabaseUser},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This enum is used to differentiate between database and user operations.
|
|
||||||
/// Their output are very similar, but there are slight differences in the words used.
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum DbOrUser {
|
|
||||||
Database,
|
|
||||||
User,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DbOrUser {
|
|
||||||
pub fn lowercased(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
DbOrUser::Database => "database",
|
|
||||||
DbOrUser::User => "user",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn capitalized(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
DbOrUser::Database => "Database",
|
|
||||||
DbOrUser::User => "User",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
|
||||||
pub enum NameValidationError {
|
|
||||||
EmptyString,
|
|
||||||
InvalidCharacters,
|
|
||||||
TooLong,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NameValidationError {
|
|
||||||
pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
|
|
||||||
match self {
|
|
||||||
NameValidationError::EmptyString => {
|
|
||||||
format!("{} name cannot be empty.", db_or_user.capitalized()).to_owned()
|
|
||||||
}
|
|
||||||
NameValidationError::TooLong => format!(
|
|
||||||
"{} is too long. Maximum length is 64 characters.",
|
|
||||||
db_or_user.capitalized()
|
|
||||||
)
|
|
||||||
.to_owned(),
|
|
||||||
NameValidationError::InvalidCharacters => format!(
|
|
||||||
indoc! {r#"
|
|
||||||
Invalid characters in {} name: '{}'
|
|
||||||
|
|
||||||
Only A-Z, a-z, 0-9, _ (underscore) and - (dash) are permitted.
|
|
||||||
"#},
|
|
||||||
db_or_user.lowercased(),
|
|
||||||
name
|
|
||||||
)
|
|
||||||
.to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnerValidationError {
|
|
||||||
pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
|
|
||||||
let user = UnixUser::from_enviroment();
|
|
||||||
|
|
||||||
let UnixUser {
|
|
||||||
username,
|
|
||||||
mut groups,
|
|
||||||
} = user.unwrap_or(UnixUser {
|
|
||||||
username: "???".to_string(),
|
|
||||||
groups: vec![],
|
|
||||||
});
|
|
||||||
|
|
||||||
groups.sort();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
OwnerValidationError::NoMatch => format!(
|
|
||||||
indoc! {r#"
|
|
||||||
Invalid {} name prefix: '{}' does not match your username or any of your groups.
|
|
||||||
Are you sure you are allowed to create {} names with this prefix?
|
|
||||||
The format should be: <prefix>_<{} name>
|
|
||||||
|
|
||||||
Allowed prefixes:
|
|
||||||
- {}
|
|
||||||
{}
|
|
||||||
"#},
|
|
||||||
db_or_user.lowercased(),
|
|
||||||
name,
|
|
||||||
db_or_user.lowercased(),
|
|
||||||
db_or_user.lowercased(),
|
|
||||||
username,
|
|
||||||
groups
|
|
||||||
.into_iter()
|
|
||||||
.filter(|g| g != &username)
|
|
||||||
.map(|g| format!(" - {}", g))
|
|
||||||
.join("\n"),
|
|
||||||
)
|
|
||||||
.to_owned(),
|
|
||||||
OwnerValidationError::StringEmpty => format!(
|
|
||||||
"'{}' is not a valid {} name.",
|
|
||||||
name,
|
|
||||||
db_or_user.lowercased()
|
|
||||||
)
|
|
||||||
.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,
|
|
||||||
|
|
||||||
// The name is empty, which is invalid
|
|
||||||
StringEmpty,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type CreateDatabasesOutput = BTreeMap<MySQLDatabase, Result<(), CreateDatabaseError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum CreateDatabaseError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
DatabaseAlreadyExists,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_create_databases_output_status(output: &CreateDatabasesOutput) {
|
|
||||||
for (database_name, result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!("Database '{}' created successfully.", database_name);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(database_name));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_create_databases_output_status_json(output: &CreateDatabasesOutput) {
|
|
||||||
let value = output
|
|
||||||
.iter()
|
|
||||||
.map(|(name, result)| match result {
|
|
||||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
|
||||||
Err(err) => (
|
|
||||||
name.to_string(),
|
|
||||||
json!({
|
|
||||||
"status": "error",
|
|
||||||
"error": err.to_error_message(name),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect::<serde_json::Map<_, _>>();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&value)
|
|
||||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateDatabaseError {
|
|
||||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
|
||||||
match self {
|
|
||||||
CreateDatabaseError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
CreateDatabaseError::OwnershipError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
CreateDatabaseError::DatabaseAlreadyExists => {
|
|
||||||
format!("Database {} already exists.", database_name)
|
|
||||||
}
|
|
||||||
CreateDatabaseError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type DropDatabasesOutput = BTreeMap<MySQLDatabase, Result<(), DropDatabaseError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum DropDatabaseError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
DatabaseDoesNotExist,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_drop_databases_output_status(output: &DropDatabasesOutput) {
|
|
||||||
for (database_name, result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!(
|
|
||||||
"Database '{}' dropped successfully.",
|
|
||||||
database_name.as_str()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(database_name));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_drop_databases_output_status_json(output: &DropDatabasesOutput) {
|
|
||||||
let value = output
|
|
||||||
.iter()
|
|
||||||
.map(|(name, result)| match result {
|
|
||||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
|
||||||
Err(err) => (
|
|
||||||
name.to_string(),
|
|
||||||
json!({
|
|
||||||
"status": "error",
|
|
||||||
"error": err.to_error_message(name),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect::<serde_json::Map<_, _>>();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&value)
|
|
||||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DropDatabaseError {
|
|
||||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
|
||||||
match self {
|
|
||||||
DropDatabaseError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
DropDatabaseError::OwnershipError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
DropDatabaseError::DatabaseDoesNotExist => {
|
|
||||||
format!("Database {} does not exist.", database_name)
|
|
||||||
}
|
|
||||||
DropDatabaseError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ListDatabasesOutput = BTreeMap<MySQLDatabase, Result<DatabaseRow, ListDatabasesError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ListDatabasesError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
DatabaseDoesNotExist,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListDatabasesError {
|
|
||||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
|
||||||
match self {
|
|
||||||
ListDatabasesError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
ListDatabasesError::OwnershipError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
ListDatabasesError::DatabaseDoesNotExist => {
|
|
||||||
format!("Database '{}' does not exist.", database_name)
|
|
||||||
}
|
|
||||||
ListDatabasesError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ListAllDatabasesOutput = Result<Vec<DatabaseRow>, ListAllDatabasesError>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ListAllDatabasesError {
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListAllDatabasesError {
|
|
||||||
pub fn to_error_message(&self) -> String {
|
|
||||||
match self {
|
|
||||||
ListAllDatabasesError::MySqlError(err) => format!("MySQL error: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: merge all rows into a single collection.
|
|
||||||
// they already contain which database they belong to.
|
|
||||||
// no need to index by database name.
|
|
||||||
|
|
||||||
pub type GetDatabasesPrivilegeData =
|
|
||||||
BTreeMap<MySQLDatabase, Result<Vec<DatabasePrivilegeRow>, GetDatabasesPrivilegeDataError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum GetDatabasesPrivilegeDataError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
DatabaseDoesNotExist,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetDatabasesPrivilegeDataError {
|
|
||||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
|
||||||
match self {
|
|
||||||
GetDatabasesPrivilegeDataError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
GetDatabasesPrivilegeDataError::OwnershipError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
GetDatabasesPrivilegeDataError::DatabaseDoesNotExist => {
|
|
||||||
format!("Database '{}' does not exist.", database_name)
|
|
||||||
}
|
|
||||||
GetDatabasesPrivilegeDataError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type GetAllDatabasesPrivilegeData =
|
|
||||||
Result<Vec<DatabasePrivilegeRow>, GetAllDatabasesPrivilegeDataError>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum GetAllDatabasesPrivilegeDataError {
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetAllDatabasesPrivilegeDataError {
|
|
||||||
pub fn to_error_message(&self) -> String {
|
|
||||||
match self {
|
|
||||||
GetAllDatabasesPrivilegeDataError::MySqlError(err) => format!("MySQL error: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ModifyDatabasePrivilegesOutput =
|
|
||||||
BTreeMap<(MySQLDatabase, MySQLUser), Result<(), ModifyDatabasePrivilegesError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ModifyDatabasePrivilegesError {
|
|
||||||
DatabaseSanitizationError(NameValidationError),
|
|
||||||
DatabaseOwnershipError(OwnerValidationError),
|
|
||||||
UserSanitizationError(NameValidationError),
|
|
||||||
UserOwnershipError(OwnerValidationError),
|
|
||||||
DatabaseDoesNotExist,
|
|
||||||
DiffDoesNotApply(DiffDoesNotApplyError),
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum DiffDoesNotApplyError {
|
|
||||||
RowAlreadyExists(MySQLDatabase, MySQLUser),
|
|
||||||
RowDoesNotExist(MySQLDatabase, MySQLUser),
|
|
||||||
RowPrivilegeChangeDoesNotApply(DatabasePrivilegeRowDiff, DatabasePrivilegeRow),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_modify_database_privileges_output_status(output: &ModifyDatabasePrivilegesOutput) {
|
|
||||||
for ((database_name, username), result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!(
|
|
||||||
"Privileges for user '{}' on database '{}' modified successfully.",
|
|
||||||
username, database_name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(database_name, username));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModifyDatabasePrivilegesError {
|
|
||||||
pub fn to_error_message(&self, database_name: &MySQLDatabase, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
ModifyDatabasePrivilegesError::DatabaseSanitizationError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
ModifyDatabasePrivilegesError::DatabaseOwnershipError(err) => {
|
|
||||||
err.to_error_message(database_name, DbOrUser::Database)
|
|
||||||
}
|
|
||||||
ModifyDatabasePrivilegesError::UserSanitizationError(err) => {
|
|
||||||
err.to_error_message(username, DbOrUser::User)
|
|
||||||
}
|
|
||||||
ModifyDatabasePrivilegesError::UserOwnershipError(err) => {
|
|
||||||
err.to_error_message(username, DbOrUser::User)
|
|
||||||
}
|
|
||||||
ModifyDatabasePrivilegesError::DatabaseDoesNotExist => {
|
|
||||||
format!("Database '{}' does not exist.", database_name)
|
|
||||||
}
|
|
||||||
ModifyDatabasePrivilegesError::DiffDoesNotApply(diff) => {
|
|
||||||
format!(
|
|
||||||
"Could not apply privilege change:\n{}",
|
|
||||||
diff.to_error_message()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ModifyDatabasePrivilegesError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DiffDoesNotApplyError {
|
|
||||||
pub fn to_error_message(&self) -> String {
|
|
||||||
match self {
|
|
||||||
DiffDoesNotApplyError::RowAlreadyExists(database_name, username) => {
|
|
||||||
format!(
|
|
||||||
"Privileges for user '{}' on database '{}' already exist.",
|
|
||||||
username, database_name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DiffDoesNotApplyError::RowDoesNotExist(database_name, username) => {
|
|
||||||
format!(
|
|
||||||
"Privileges for user '{}' on database '{}' do not exist.",
|
|
||||||
username, database_name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DiffDoesNotApplyError::RowPrivilegeChangeDoesNotApply(diff, row) => {
|
|
||||||
format!(
|
|
||||||
"Could not apply privilege change {:?} to row {:?}",
|
|
||||||
diff, row
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type CreateUsersOutput = BTreeMap<MySQLUser, Result<(), CreateUserError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum CreateUserError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
UserAlreadyExists,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_create_users_output_status(output: &CreateUsersOutput) {
|
|
||||||
for (username, result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!("User '{}' created successfully.", username);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(username));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_create_users_output_status_json(output: &CreateUsersOutput) {
|
|
||||||
let value = output
|
|
||||||
.iter()
|
|
||||||
.map(|(name, result)| match result {
|
|
||||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
|
||||||
Err(err) => (
|
|
||||||
name.to_string(),
|
|
||||||
json!({
|
|
||||||
"status": "error",
|
|
||||||
"error": err.to_error_message(name),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect::<serde_json::Map<_, _>>();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&value)
|
|
||||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateUserError {
|
|
||||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
CreateUserError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(username, DbOrUser::User)
|
|
||||||
}
|
|
||||||
CreateUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
CreateUserError::UserAlreadyExists => {
|
|
||||||
format!("User '{}' already exists.", username)
|
|
||||||
}
|
|
||||||
CreateUserError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type DropUsersOutput = BTreeMap<MySQLUser, Result<(), DropUserError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum DropUserError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
UserDoesNotExist,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_drop_users_output_status(output: &DropUsersOutput) {
|
|
||||||
for (username, result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!("User '{}' dropped successfully.", username);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(username));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_drop_users_output_status_json(output: &DropUsersOutput) {
|
|
||||||
let value = output
|
|
||||||
.iter()
|
|
||||||
.map(|(name, result)| match result {
|
|
||||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
|
||||||
Err(err) => (
|
|
||||||
name.to_string(),
|
|
||||||
json!({
|
|
||||||
"status": "error",
|
|
||||||
"error": err.to_error_message(name),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect::<serde_json::Map<_, _>>();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&value)
|
|
||||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DropUserError {
|
|
||||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
DropUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
DropUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
DropUserError::UserDoesNotExist => {
|
|
||||||
format!("User '{}' does not exist.", username)
|
|
||||||
}
|
|
||||||
DropUserError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type SetPasswordOutput = Result<(), SetPasswordError>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum SetPasswordError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
UserDoesNotExist,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_set_password_output_status(output: &SetPasswordOutput, username: &MySQLUser) {
|
|
||||||
match output {
|
|
||||||
Ok(()) => {
|
|
||||||
println!("Password for user '{}' set successfully.", username);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(username));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SetPasswordError {
|
|
||||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
SetPasswordError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(username, DbOrUser::User)
|
|
||||||
}
|
|
||||||
SetPasswordError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
SetPasswordError::UserDoesNotExist => {
|
|
||||||
format!("User '{}' does not exist.", username)
|
|
||||||
}
|
|
||||||
SetPasswordError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type LockUsersOutput = BTreeMap<MySQLUser, Result<(), LockUserError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum LockUserError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
UserDoesNotExist,
|
|
||||||
UserIsAlreadyLocked,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_lock_users_output_status(output: &LockUsersOutput) {
|
|
||||||
for (username, result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!("User '{}' locked successfully.", username);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(username));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_lock_users_output_status_json(output: &LockUsersOutput) {
|
|
||||||
let value = output
|
|
||||||
.iter()
|
|
||||||
.map(|(name, result)| match result {
|
|
||||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
|
||||||
Err(err) => (
|
|
||||||
name.to_string(),
|
|
||||||
json!({
|
|
||||||
"status": "error",
|
|
||||||
"error": err.to_error_message(name),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect::<serde_json::Map<_, _>>();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&value)
|
|
||||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LockUserError {
|
|
||||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
LockUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
LockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
LockUserError::UserDoesNotExist => {
|
|
||||||
format!("User '{}' does not exist.", username)
|
|
||||||
}
|
|
||||||
LockUserError::UserIsAlreadyLocked => {
|
|
||||||
format!("User '{}' is already locked.", username)
|
|
||||||
}
|
|
||||||
LockUserError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type UnlockUsersOutput = BTreeMap<MySQLUser, Result<(), UnlockUserError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum UnlockUserError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
UserDoesNotExist,
|
|
||||||
UserIsAlreadyUnlocked,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_unlock_users_output_status(output: &UnlockUsersOutput) {
|
|
||||||
for (username, result) in output {
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
println!("User '{}' unlocked successfully.", username);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("{}", err.to_error_message(username));
|
|
||||||
println!("Skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_unlock_users_output_status_json(output: &UnlockUsersOutput) {
|
|
||||||
let value = output
|
|
||||||
.iter()
|
|
||||||
.map(|(name, result)| match result {
|
|
||||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
|
||||||
Err(err) => (
|
|
||||||
name.to_string(),
|
|
||||||
json!({
|
|
||||||
"status": "error",
|
|
||||||
"error": err.to_error_message(name),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect::<serde_json::Map<_, _>>();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&value)
|
|
||||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnlockUserError {
|
|
||||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
UnlockUserError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(username, DbOrUser::User)
|
|
||||||
}
|
|
||||||
UnlockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
UnlockUserError::UserDoesNotExist => {
|
|
||||||
format!("User '{}' does not exist.", username)
|
|
||||||
}
|
|
||||||
UnlockUserError::UserIsAlreadyUnlocked => {
|
|
||||||
format!("User '{}' is already unlocked.", username)
|
|
||||||
}
|
|
||||||
UnlockUserError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ListUsersOutput = BTreeMap<MySQLUser, Result<DatabaseUser, ListUsersError>>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ListUsersError {
|
|
||||||
SanitizationError(NameValidationError),
|
|
||||||
OwnershipError(OwnerValidationError),
|
|
||||||
UserDoesNotExist,
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListUsersError {
|
|
||||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
|
||||||
match self {
|
|
||||||
ListUsersError::SanitizationError(err) => {
|
|
||||||
err.to_error_message(username, DbOrUser::User)
|
|
||||||
}
|
|
||||||
ListUsersError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
|
||||||
ListUsersError::UserDoesNotExist => {
|
|
||||||
format!("User '{}' does not exist.", username)
|
|
||||||
}
|
|
||||||
ListUsersError::MySqlError(err) => {
|
|
||||||
format!("MySQL error: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ListAllUsersOutput = Result<Vec<DatabaseUser>, ListAllUsersError>;
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ListAllUsersError {
|
|
||||||
MySqlError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListAllUsersError {
|
|
||||||
pub fn to_error_message(&self) -> String {
|
|
||||||
match self {
|
|
||||||
ListAllUsersError::MySqlError(err) => format!("MySQL error: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::core::{
|
use crate::core::{
|
||||||
common::UnixUser,
|
common::UnixUser,
|
||||||
protocol::server_responses::{NameValidationError, OwnerValidationError},
|
protocol::request_validation::{NameValidationError, OwnerValidationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_NAME_LENGTH: usize = 64;
|
const MAX_NAME_LENGTH: usize = 64;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::server::sql::database_operations::list_databases;
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
common::{DEFAULT_SOCKET_PATH, UnixUser},
|
common::{DEFAULT_SOCKET_PATH, UnixUser},
|
||||||
protocol::request_response::{
|
protocol::{
|
||||||
Request, Response, ServerToClientMessageStream, create_server_to_client_message_stream,
|
Request, Response, ServerToClientMessageStream, create_server_to_client_message_stream,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -233,8 +233,8 @@ async fn handle_requests_for_single_session_with_db_connection(
|
|||||||
|
|
||||||
// TODO: don't clone the request
|
// TODO: don't clone the request
|
||||||
let request_to_display = match &request {
|
let request_to_display = match &request {
|
||||||
Request::PasswdUser(db_user, _) => {
|
Request::PasswdUser((db_user, _)) => {
|
||||||
Request::PasswdUser(db_user.to_owned(), "<REDACTED>".to_string())
|
Request::PasswdUser((db_user.to_owned(), "<REDACTED>".to_string()))
|
||||||
}
|
}
|
||||||
request => request.to_owned(),
|
request => request.to_owned(),
|
||||||
};
|
};
|
||||||
@@ -289,11 +289,11 @@ async fn handle_requests_for_single_session_with_db_connection(
|
|||||||
let result = drop_database_users(db_users, unix_user, db_connection).await;
|
let result = drop_database_users(db_users, unix_user, db_connection).await;
|
||||||
Response::DropUsers(result)
|
Response::DropUsers(result)
|
||||||
}
|
}
|
||||||
Request::PasswdUser(db_user, password) => {
|
Request::PasswdUser((db_user, password)) => {
|
||||||
let result =
|
let result =
|
||||||
set_password_for_database_user(&db_user, &password, unix_user, db_connection)
|
set_password_for_database_user(&db_user, &password, unix_user, db_connection)
|
||||||
.await;
|
.await;
|
||||||
Response::PasswdUser(result)
|
Response::SetUserPassword(result)
|
||||||
}
|
}
|
||||||
Request::ListUsers(db_users) => match db_users {
|
Request::ListUsers(db_users) => match db_users {
|
||||||
Some(db_users) => {
|
Some(db_users) => {
|
||||||
@@ -321,8 +321,10 @@ async fn handle_requests_for_single_session_with_db_connection(
|
|||||||
|
|
||||||
// TODO: don't clone the response
|
// TODO: don't clone the response
|
||||||
let response_to_display = match &response {
|
let response_to_display = match &response {
|
||||||
Response::PasswdUser(Err(SetPasswordError::MySqlError(_))) => {
|
Response::SetUserPassword(Err(SetPasswordError::MySqlError(_))) => {
|
||||||
Response::PasswdUser(Err(SetPasswordError::MySqlError("<REDACTED>".to_string())))
|
Response::SetUserPassword(Err(SetPasswordError::MySqlError(
|
||||||
|
"<REDACTED>".to_string(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
response => response.to_owned(),
|
response => response.to_owned(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ use crate::{
|
|||||||
core::{
|
core::{
|
||||||
common::UnixUser,
|
common::UnixUser,
|
||||||
protocol::{
|
protocol::{
|
||||||
CreateDatabaseError, CreateDatabasesOutput, DropDatabaseError, DropDatabasesOutput,
|
CreateDatabaseError, CreateDatabasesResponse, DropDatabaseError, DropDatabasesResponse,
|
||||||
ListAllDatabasesError, ListAllDatabasesOutput, ListDatabasesError, ListDatabasesOutput,
|
ListAllDatabasesError, ListAllDatabasesResponse, ListDatabasesError,
|
||||||
|
ListDatabasesResponse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server::{
|
server::{
|
||||||
@@ -46,7 +47,7 @@ pub async fn create_databases(
|
|||||||
database_names: Vec<MySQLDatabase>,
|
database_names: Vec<MySQLDatabase>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> CreateDatabasesOutput {
|
) -> CreateDatabasesResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for database_name in database_names {
|
for database_name in database_names {
|
||||||
@@ -105,7 +106,7 @@ pub async fn drop_databases(
|
|||||||
database_names: Vec<MySQLDatabase>,
|
database_names: Vec<MySQLDatabase>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> DropDatabasesOutput {
|
) -> DropDatabasesResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for database_name in database_names {
|
for database_name in database_names {
|
||||||
@@ -177,7 +178,7 @@ pub async fn list_databases(
|
|||||||
database_names: Vec<MySQLDatabase>,
|
database_names: Vec<MySQLDatabase>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> ListDatabasesOutput {
|
) -> ListDatabasesResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for database_name in database_names {
|
for database_name in database_names {
|
||||||
@@ -227,7 +228,7 @@ pub async fn list_databases(
|
|||||||
pub async fn list_all_databases_for_user(
|
pub async fn list_all_databases_for_user(
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> ListAllDatabasesOutput {
|
) -> ListAllDatabasesResponse {
|
||||||
let result = sqlx::query_as::<_, DatabaseRow>(
|
let result = sqlx::query_as::<_, DatabaseRow>(
|
||||||
r#"
|
r#"
|
||||||
SELECT `SCHEMA_NAME` AS `database`
|
SELECT `SCHEMA_NAME` AS `database`
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ use crate::{
|
|||||||
DatabasePrivilegesDiff,
|
DatabasePrivilegesDiff,
|
||||||
},
|
},
|
||||||
protocol::{
|
protocol::{
|
||||||
DiffDoesNotApplyError, GetAllDatabasesPrivilegeData, GetAllDatabasesPrivilegeDataError,
|
DiffDoesNotApplyError, GetAllDatabasesPrivilegeDataError,
|
||||||
GetDatabasesPrivilegeData, GetDatabasesPrivilegeDataError,
|
GetDatabasesPrivilegeDataError, ListAllPrivilegesResponse, ListPrivilegesResponse,
|
||||||
ModifyDatabasePrivilegesError, ModifyDatabasePrivilegesOutput,
|
ModifyDatabasePrivilegesError, ModifyPrivilegesResponse,
|
||||||
},
|
},
|
||||||
types::{MySQLDatabase, MySQLUser},
|
types::{MySQLDatabase, MySQLUser},
|
||||||
},
|
},
|
||||||
@@ -139,7 +139,7 @@ pub async fn get_databases_privilege_data(
|
|||||||
database_names: Vec<MySQLDatabase>,
|
database_names: Vec<MySQLDatabase>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> GetDatabasesPrivilegeData {
|
) -> ListPrivilegesResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for database_name in database_names.iter() {
|
for database_name in database_names.iter() {
|
||||||
@@ -186,7 +186,7 @@ pub async fn get_databases_privilege_data(
|
|||||||
pub async fn get_all_database_privileges(
|
pub async fn get_all_database_privileges(
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> GetAllDatabasesPrivilegeData {
|
) -> ListAllPrivilegesResponse {
|
||||||
let result = sqlx::query_as::<_, DatabasePrivilegeRow>(&format!(
|
let result = sqlx::query_as::<_, DatabasePrivilegeRow>(&format!(
|
||||||
indoc! {r#"
|
indoc! {r#"
|
||||||
SELECT {} FROM `db` WHERE `db` IN
|
SELECT {} FROM `db` WHERE `db` IN
|
||||||
@@ -393,7 +393,7 @@ pub async fn apply_privilege_diffs(
|
|||||||
database_privilege_diffs: BTreeSet<DatabasePrivilegesDiff>,
|
database_privilege_diffs: BTreeSet<DatabasePrivilegesDiff>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> ModifyDatabasePrivilegesOutput {
|
) -> ModifyPrivilegesResponse {
|
||||||
let mut results: BTreeMap<(MySQLDatabase, MySQLUser), _> = BTreeMap::new();
|
let mut results: BTreeMap<(MySQLDatabase, MySQLUser), _> = BTreeMap::new();
|
||||||
|
|
||||||
for diff in database_privilege_diffs {
|
for diff in database_privilege_diffs {
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ use crate::{
|
|||||||
common::UnixUser,
|
common::UnixUser,
|
||||||
database_privileges::DATABASE_PRIVILEGE_FIELDS,
|
database_privileges::DATABASE_PRIVILEGE_FIELDS,
|
||||||
protocol::{
|
protocol::{
|
||||||
CreateUserError, CreateUsersOutput, DropUserError, DropUsersOutput, ListAllUsersError,
|
CreateUserError, CreateUsersResponse, DropUserError, DropUsersResponse,
|
||||||
ListAllUsersOutput, ListUsersError, ListUsersOutput, LockUserError, LockUsersOutput,
|
ListAllUsersError, ListAllUsersResponse, ListUsersError, ListUsersResponse,
|
||||||
SetPasswordError, SetPasswordOutput, UnlockUserError, UnlockUsersOutput,
|
LockUserError, LockUsersResponse, SetPasswordError, SetUserPasswordResponse,
|
||||||
|
UnlockUserError, UnlockUsersResponse,
|
||||||
},
|
},
|
||||||
types::MySQLUser,
|
types::MySQLUser,
|
||||||
},
|
},
|
||||||
@@ -54,7 +55,7 @@ pub async fn create_database_users(
|
|||||||
db_users: Vec<MySQLUser>,
|
db_users: Vec<MySQLUser>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> CreateUsersOutput {
|
) -> CreateUsersResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for db_user in db_users {
|
for db_user in db_users {
|
||||||
@@ -100,7 +101,7 @@ pub async fn drop_database_users(
|
|||||||
db_users: Vec<MySQLUser>,
|
db_users: Vec<MySQLUser>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> DropUsersOutput {
|
) -> DropUsersResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for db_user in db_users {
|
for db_user in db_users {
|
||||||
@@ -147,7 +148,7 @@ pub async fn set_password_for_database_user(
|
|||||||
password: &str,
|
password: &str,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> SetPasswordOutput {
|
) -> SetUserPasswordResponse {
|
||||||
if let Err(err) = validate_name(db_user) {
|
if let Err(err) = validate_name(db_user) {
|
||||||
return Err(SetPasswordError::SanitizationError(err));
|
return Err(SetPasswordError::SanitizationError(err));
|
||||||
}
|
}
|
||||||
@@ -221,7 +222,7 @@ pub async fn lock_database_users(
|
|||||||
db_users: Vec<MySQLUser>,
|
db_users: Vec<MySQLUser>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> LockUsersOutput {
|
) -> LockUsersResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for db_user in db_users {
|
for db_user in db_users {
|
||||||
@@ -281,7 +282,7 @@ pub async fn unlock_database_users(
|
|||||||
db_users: Vec<MySQLUser>,
|
db_users: Vec<MySQLUser>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> UnlockUsersOutput {
|
) -> UnlockUsersResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for db_user in db_users {
|
for db_user in db_users {
|
||||||
@@ -380,7 +381,7 @@ pub async fn list_database_users(
|
|||||||
db_users: Vec<MySQLUser>,
|
db_users: Vec<MySQLUser>,
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> ListUsersOutput {
|
) -> ListUsersResponse {
|
||||||
let mut results = BTreeMap::new();
|
let mut results = BTreeMap::new();
|
||||||
|
|
||||||
for db_user in db_users {
|
for db_user in db_users {
|
||||||
@@ -422,7 +423,7 @@ pub async fn list_database_users(
|
|||||||
pub async fn list_all_database_users_for_unix_user(
|
pub async fn list_all_database_users_for_unix_user(
|
||||||
unix_user: &UnixUser,
|
unix_user: &UnixUser,
|
||||||
connection: &mut MySqlConnection,
|
connection: &mut MySqlConnection,
|
||||||
) -> ListAllUsersOutput {
|
) -> ListAllUsersResponse {
|
||||||
let mut result = sqlx::query_as::<_, DatabaseUser>(
|
let mut result = sqlx::query_as::<_, DatabaseUser>(
|
||||||
&(DB_USER_SELECT_STATEMENT.to_string() + "WHERE `user`.`User` REGEXP ?"),
|
&(DB_USER_SELECT_STATEMENT.to_string() + "WHERE `user`.`User` REGEXP ?"),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user