clippy pedantic fix + get rid of a few unwraps
All checks were successful
All checks were successful
This commit is contained in:
@@ -42,10 +42,8 @@ pub async fn check_authorization(
|
||||
/// - `gid:1001`
|
||||
/// - `group:admins`
|
||||
pub fn read_and_parse_group_denylist(denylist_path: &Path) -> anyhow::Result<GroupDenylist> {
|
||||
let content = std::fs::read_to_string(denylist_path).context(format!(
|
||||
"Failed to read denylist file at {:?}",
|
||||
denylist_path
|
||||
))?;
|
||||
let content = std::fs::read_to_string(denylist_path)
|
||||
.context(format!("Failed to read denylist file at {denylist_path:?}"))?;
|
||||
|
||||
let mut groups = HashSet::with_capacity(content.lines().count());
|
||||
|
||||
@@ -128,7 +126,6 @@ pub fn read_and_parse_group_denylist(denylist_path: &Path) -> anyhow::Result<Gro
|
||||
line_number + 1,
|
||||
err
|
||||
);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
||||
@@ -44,7 +44,7 @@ impl MysqlConfig {
|
||||
if let Some(password_file) = &self.password_file {
|
||||
let password = fs::read_to_string(password_file)
|
||||
.with_context(|| {
|
||||
format!("Failed to read MySQL password file at {:?}", password_file)
|
||||
format!("Failed to read MySQL password file at {password_file:?}")
|
||||
})?
|
||||
.trim()
|
||||
.to_owned();
|
||||
@@ -96,8 +96,8 @@ impl ServerConfig {
|
||||
tracing::debug!("Reading config file at {:?}", config_path);
|
||||
|
||||
fs::read_to_string(config_path)
|
||||
.context(format!("Failed to read config file at {:?}", config_path))
|
||||
.context(format!("Failed to read config file at {config_path:?}"))
|
||||
.and_then(|c| toml::from_str(&c).context("Failed to parse config file"))
|
||||
.context(format!("Failed to parse config file at {:?}", config_path))
|
||||
.context(format!("Failed to parse config file at {config_path:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ pub async fn session_handler(
|
||||
))
|
||||
.await
|
||||
.ok();
|
||||
anyhow::bail!("Failed to get username from uid: {}", e);
|
||||
anyhow::bail!("Failed to get username from uid: {e}");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,10 +181,10 @@ async fn session_handler_with_db_connection(
|
||||
request => request.to_owned(),
|
||||
};
|
||||
|
||||
if request_to_display != Request::Exit {
|
||||
tracing::info!("Received request: {:#?}", request_to_display);
|
||||
} else {
|
||||
if request_to_display == Request::Exit {
|
||||
tracing::debug!("Received request: {:#?}", request_to_display);
|
||||
} else {
|
||||
tracing::info!("Received request: {:#?}", request_to_display);
|
||||
}
|
||||
|
||||
let response = match request {
|
||||
@@ -194,22 +194,20 @@ async fn session_handler_with_db_connection(
|
||||
}
|
||||
Request::ListValidNamePrefixes => {
|
||||
let mut result = Vec::with_capacity(unix_user.groups.len() + 1);
|
||||
result.push(unix_user.username.to_owned());
|
||||
result.push(unix_user.username.clone());
|
||||
|
||||
for group in get_user_filtered_groups(unix_user, group_denylist) {
|
||||
result.push(group.to_owned());
|
||||
result.push(group.clone());
|
||||
}
|
||||
|
||||
Response::ListValidNamePrefixes(result)
|
||||
}
|
||||
Request::CompleteDatabaseName(partial_database_name) => {
|
||||
// TODO: more correct validation here
|
||||
if !partial_database_name
|
||||
if partial_database_name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
||||
{
|
||||
Response::CompleteDatabaseName(vec![])
|
||||
} else {
|
||||
let result = complete_database_name(
|
||||
partial_database_name,
|
||||
unix_user,
|
||||
@@ -219,16 +217,16 @@ async fn session_handler_with_db_connection(
|
||||
)
|
||||
.await;
|
||||
Response::CompleteDatabaseName(result)
|
||||
} else {
|
||||
Response::CompleteDatabaseName(vec![])
|
||||
}
|
||||
}
|
||||
Request::CompleteUserName(partial_user_name) => {
|
||||
// TODO: more correct validation here
|
||||
if !partial_user_name
|
||||
if partial_user_name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
||||
{
|
||||
Response::CompleteUserName(vec![])
|
||||
} else {
|
||||
let result = complete_user_name(
|
||||
partial_user_name,
|
||||
unix_user,
|
||||
@@ -238,6 +236,8 @@ async fn session_handler_with_db_connection(
|
||||
)
|
||||
.await;
|
||||
Response::CompleteUserName(result)
|
||||
} else {
|
||||
Response::CompleteUserName(vec![])
|
||||
}
|
||||
}
|
||||
Request::CreateDatabases(databases_names) => {
|
||||
@@ -262,8 +262,8 @@ async fn session_handler_with_db_connection(
|
||||
.await;
|
||||
Response::DropDatabases(result)
|
||||
}
|
||||
Request::ListDatabases(database_names) => match database_names {
|
||||
Some(database_names) => {
|
||||
Request::ListDatabases(database_names) => {
|
||||
if let Some(database_names) = database_names {
|
||||
let result = list_databases(
|
||||
database_names,
|
||||
unix_user,
|
||||
@@ -273,8 +273,7 @@ async fn session_handler_with_db_connection(
|
||||
)
|
||||
.await;
|
||||
Response::ListDatabases(result)
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
let result = list_all_databases_for_user(
|
||||
unix_user,
|
||||
db_connection,
|
||||
@@ -284,9 +283,9 @@ async fn session_handler_with_db_connection(
|
||||
.await;
|
||||
Response::ListAllDatabases(result)
|
||||
}
|
||||
},
|
||||
Request::ListPrivileges(database_names) => match database_names {
|
||||
Some(database_names) => {
|
||||
}
|
||||
Request::ListPrivileges(database_names) => {
|
||||
if let Some(database_names) = database_names {
|
||||
let privilege_data = get_databases_privilege_data(
|
||||
database_names,
|
||||
unix_user,
|
||||
@@ -296,8 +295,7 @@ async fn session_handler_with_db_connection(
|
||||
)
|
||||
.await;
|
||||
Response::ListPrivileges(privilege_data)
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
let privilege_data = get_all_database_privileges(
|
||||
unix_user,
|
||||
db_connection,
|
||||
@@ -307,7 +305,7 @@ async fn session_handler_with_db_connection(
|
||||
.await;
|
||||
Response::ListAllPrivileges(privilege_data)
|
||||
}
|
||||
},
|
||||
}
|
||||
Request::ModifyPrivileges(database_privilege_diffs) => {
|
||||
let result = apply_privilege_diffs(
|
||||
BTreeSet::from_iter(database_privilege_diffs),
|
||||
@@ -353,8 +351,8 @@ async fn session_handler_with_db_connection(
|
||||
.await;
|
||||
Response::SetUserPassword(result)
|
||||
}
|
||||
Request::ListUsers(db_users) => match db_users {
|
||||
Some(db_users) => {
|
||||
Request::ListUsers(db_users) => {
|
||||
if let Some(db_users) = db_users {
|
||||
let result = list_database_users(
|
||||
db_users,
|
||||
unix_user,
|
||||
@@ -364,8 +362,7 @@ async fn session_handler_with_db_connection(
|
||||
)
|
||||
.await;
|
||||
Response::ListUsers(result)
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
let result = list_all_database_users_for_unix_user(
|
||||
unix_user,
|
||||
db_connection,
|
||||
@@ -375,7 +372,7 @@ async fn session_handler_with_db_connection(
|
||||
.await;
|
||||
Response::ListAllUsers(result)
|
||||
}
|
||||
},
|
||||
}
|
||||
Request::LockUsers(db_users) => {
|
||||
let result = lock_database_users(
|
||||
db_users,
|
||||
|
||||
@@ -3,11 +3,13 @@ pub mod database_privilege_operations;
|
||||
pub mod user_operations;
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn quote_literal(s: &str) -> String {
|
||||
format!("'{}'", s.replace('\'', r"\'"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn quote_identifier(s: &str) -> String {
|
||||
format!("`{}`", s.replace('`', r"\`"))
|
||||
}
|
||||
|
||||
@@ -53,16 +53,16 @@ pub async fn complete_database_name(
|
||||
group_denylist: &GroupDenylist,
|
||||
) -> CompleteDatabaseNameResponse {
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
r"
|
||||
SELECT CAST(`SCHEMA_NAME` AS CHAR(64)) AS `database`
|
||||
FROM `information_schema`.`SCHEMATA`
|
||||
WHERE `SCHEMA_NAME` NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
|
||||
AND `SCHEMA_NAME` REGEXP ?
|
||||
AND `SCHEMA_NAME` LIKE ?
|
||||
"#,
|
||||
",
|
||||
)
|
||||
.bind(create_user_group_matching_regex(unix_user, group_denylist))
|
||||
.bind(format!("{}%", database_prefix))
|
||||
.bind(format!("{database_prefix}%"))
|
||||
.fetch_all(connection)
|
||||
.await;
|
||||
|
||||
@@ -103,21 +103,21 @@ pub async fn create_databases(
|
||||
)
|
||||
.map_err(CreateDatabaseError::ValidationError)
|
||||
{
|
||||
results.insert(database_name.to_owned(), Err(err));
|
||||
results.insert(database_name.clone(), Err(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
match unsafe_database_exists(&database_name, &mut *connection).await {
|
||||
Ok(true) => {
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
database_name.clone(),
|
||||
Err(CreateDatabaseError::DatabaseAlreadyExists),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
database_name.clone(),
|
||||
Err(CreateDatabaseError::MySqlError(err.to_string())),
|
||||
);
|
||||
continue;
|
||||
@@ -159,21 +159,21 @@ pub async fn drop_databases(
|
||||
)
|
||||
.map_err(DropDatabaseError::ValidationError)
|
||||
{
|
||||
results.insert(database_name.to_owned(), Err(err));
|
||||
results.insert(database_name.clone(), Err(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
match unsafe_database_exists(&database_name, &mut *connection).await {
|
||||
Ok(false) => {
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
database_name.clone(),
|
||||
Err(DropDatabaseError::DatabaseDoesNotExist),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
database_name.clone(),
|
||||
Err(DropDatabaseError::MySqlError(err.to_string())),
|
||||
);
|
||||
continue;
|
||||
@@ -218,7 +218,7 @@ impl FromRow<'_, sqlx::mysql::MySqlRow> for DatabaseRow {
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(s.split(',').map(|s| s.to_owned()).collect())
|
||||
Some(s.split(',').map(std::borrow::ToOwned::to_owned).collect())
|
||||
}
|
||||
})
|
||||
.unwrap_or_default()
|
||||
@@ -258,12 +258,12 @@ pub async fn list_databases(
|
||||
)
|
||||
.map_err(ListDatabasesError::ValidationError)
|
||||
{
|
||||
results.insert(database_name.to_owned(), Err(err));
|
||||
results.insert(database_name.clone(), Err(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = sqlx::query_as::<_, DatabaseRow>(
|
||||
r#"
|
||||
r"
|
||||
SELECT
|
||||
CAST(`information_schema`.`SCHEMATA`.`SCHEMA_NAME` AS CHAR(64)) AS `database`,
|
||||
GROUP_CONCAT(DISTINCT CAST(`information_schema`.`TABLES`.`TABLE_NAME` AS CHAR(64)) SEPARATOR ',') AS `tables`,
|
||||
@@ -281,7 +281,7 @@ pub async fn list_databases(
|
||||
ON `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = `mysql`.`db`.`DB`
|
||||
WHERE `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = ?
|
||||
GROUP BY `information_schema`.`SCHEMATA`.`SCHEMA_NAME`
|
||||
"#,
|
||||
",
|
||||
|
||||
)
|
||||
.bind(database_name.to_string())
|
||||
@@ -289,9 +289,7 @@ pub async fn list_databases(
|
||||
.await
|
||||
.map_err(|err| ListDatabasesError::MySqlError(err.to_string()))
|
||||
.and_then(|database| {
|
||||
database
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| Err(ListDatabasesError::DatabaseDoesNotExist))
|
||||
database.map_or_else(|| Err(ListDatabasesError::DatabaseDoesNotExist), Ok)
|
||||
});
|
||||
|
||||
if let Err(err) = &result {
|
||||
@@ -313,7 +311,7 @@ pub async fn list_all_databases_for_user(
|
||||
group_denylist: &GroupDenylist,
|
||||
) -> ListAllDatabasesResponse {
|
||||
let result = sqlx::query_as::<_, DatabaseRow>(
|
||||
r#"
|
||||
r"
|
||||
SELECT
|
||||
CAST(`information_schema`.`SCHEMATA`.`SCHEMA_NAME` AS CHAR(64)) AS `database`,
|
||||
GROUP_CONCAT(DISTINCT CAST(`information_schema`.`TABLES`.`TABLE_NAME` AS CHAR(64)) SEPARATOR ',') AS `tables`,
|
||||
@@ -332,7 +330,7 @@ pub async fn list_all_databases_for_user(
|
||||
WHERE `information_schema`.`SCHEMATA`.`SCHEMA_NAME` NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
|
||||
AND `information_schema`.`SCHEMATA`.`SCHEMA_NAME` REGEXP ?
|
||||
GROUP BY `information_schema`.`SCHEMATA`.`SCHEMA_NAME`
|
||||
"#,
|
||||
",
|
||||
)
|
||||
.bind(create_user_group_matching_regex(unix_user, group_denylist))
|
||||
.fetch_all(connection)
|
||||
|
||||
@@ -50,12 +50,11 @@ use crate::{
|
||||
fn get_mysql_row_priv_field(row: &MySqlRow, position: usize) -> Result<bool, sqlx::Error> {
|
||||
let field = DATABASE_PRIVILEGE_FIELDS[position];
|
||||
let value = row.try_get(position)?;
|
||||
match rev_yn(value) {
|
||||
Some(val) => Ok(val),
|
||||
_ => {
|
||||
tracing::warn!(r#"Invalid value for privilege "{}": '{}'"#, field, value);
|
||||
Ok(false)
|
||||
}
|
||||
if let Some(val) = rev_yn(value) {
|
||||
Ok(val)
|
||||
} else {
|
||||
tracing::warn!(r#"Invalid value for privilege "{}": '{}'"#, field, value);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +146,7 @@ pub async fn get_databases_privilege_data(
|
||||
) -> ListPrivilegesResponse {
|
||||
let mut results = BTreeMap::new();
|
||||
|
||||
for database_name in database_names.iter() {
|
||||
for database_name in &database_names {
|
||||
if let Err(err) = validate_db_or_user_request(
|
||||
&DbOrUser::Database(database_name.clone()),
|
||||
unix_user,
|
||||
@@ -159,15 +158,22 @@ pub async fn get_databases_privilege_data(
|
||||
continue;
|
||||
}
|
||||
|
||||
if !unsafe_database_exists(database_name, connection)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
Err(ListPrivilegesError::DatabaseDoesNotExist),
|
||||
);
|
||||
continue;
|
||||
match unsafe_database_exists(database_name, connection).await {
|
||||
Ok(false) => {
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
Err(ListPrivilegesError::DatabaseDoesNotExist),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
results.insert(
|
||||
database_name.to_owned(),
|
||||
Err(ListPrivilegesError::MySqlError(e.to_string())),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Ok(true) => {}
|
||||
}
|
||||
|
||||
let result = unsafe_get_database_privileges(database_name, connection)
|
||||
@@ -185,13 +191,13 @@ pub async fn get_databases_privilege_data(
|
||||
/// TODO: make this constant
|
||||
fn get_all_db_privs_query() -> String {
|
||||
format!(
|
||||
indoc! {r#"
|
||||
indoc! {r"
|
||||
SELECT {} FROM `db` WHERE `db` IN
|
||||
(SELECT DISTINCT CAST(`SCHEMA_NAME` AS CHAR(64)) AS `database`
|
||||
FROM `information_schema`.`SCHEMATA`
|
||||
WHERE `SCHEMA_NAME` NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
|
||||
AND `SCHEMA_NAME` REGEXP ?)
|
||||
"#},
|
||||
"},
|
||||
DATABASE_PRIVILEGE_FIELDS
|
||||
.iter()
|
||||
.map(|field| quote_identifier(field))
|
||||
@@ -234,25 +240,23 @@ async fn unsafe_apply_privilege_diff(
|
||||
let question_marks =
|
||||
std::iter::repeat_n("?", DATABASE_PRIVILEGE_FIELDS.len()).join(",");
|
||||
|
||||
sqlx::query(
|
||||
format!("INSERT INTO `db` ({}) VALUES ({})", tables, question_marks).as_str(),
|
||||
)
|
||||
.bind(p.db.to_string())
|
||||
.bind(p.user.to_string())
|
||||
.bind(yn(p.select_priv))
|
||||
.bind(yn(p.insert_priv))
|
||||
.bind(yn(p.update_priv))
|
||||
.bind(yn(p.delete_priv))
|
||||
.bind(yn(p.create_priv))
|
||||
.bind(yn(p.drop_priv))
|
||||
.bind(yn(p.alter_priv))
|
||||
.bind(yn(p.index_priv))
|
||||
.bind(yn(p.create_tmp_table_priv))
|
||||
.bind(yn(p.lock_tables_priv))
|
||||
.bind(yn(p.references_priv))
|
||||
.execute(connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
sqlx::query(format!("INSERT INTO `db` ({tables}) VALUES ({question_marks})").as_str())
|
||||
.bind(p.db.to_string())
|
||||
.bind(p.user.to_string())
|
||||
.bind(yn(p.select_priv))
|
||||
.bind(yn(p.insert_priv))
|
||||
.bind(yn(p.update_priv))
|
||||
.bind(yn(p.delete_priv))
|
||||
.bind(yn(p.create_priv))
|
||||
.bind(yn(p.drop_priv))
|
||||
.bind(yn(p.alter_priv))
|
||||
.bind(yn(p.index_priv))
|
||||
.bind(yn(p.create_tmp_table_priv))
|
||||
.bind(yn(p.lock_tables_priv))
|
||||
.bind(yn(p.references_priv))
|
||||
.execute(connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
DatabasePrivilegesDiff::Modified(p) => {
|
||||
let changes = DATABASE_PRIVILEGE_FIELDS
|
||||
@@ -274,25 +278,23 @@ async fn unsafe_apply_privilege_diff(
|
||||
}
|
||||
}
|
||||
|
||||
sqlx::query(
|
||||
format!("UPDATE `db` SET {} WHERE `Db` = ? AND `User` = ?", changes).as_str(),
|
||||
)
|
||||
.bind(p.select_priv.map(change_to_yn))
|
||||
.bind(p.insert_priv.map(change_to_yn))
|
||||
.bind(p.update_priv.map(change_to_yn))
|
||||
.bind(p.delete_priv.map(change_to_yn))
|
||||
.bind(p.create_priv.map(change_to_yn))
|
||||
.bind(p.drop_priv.map(change_to_yn))
|
||||
.bind(p.alter_priv.map(change_to_yn))
|
||||
.bind(p.index_priv.map(change_to_yn))
|
||||
.bind(p.create_tmp_table_priv.map(change_to_yn))
|
||||
.bind(p.lock_tables_priv.map(change_to_yn))
|
||||
.bind(p.references_priv.map(change_to_yn))
|
||||
.bind(p.db.to_string())
|
||||
.bind(p.user.to_string())
|
||||
.execute(connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
sqlx::query(format!("UPDATE `db` SET {changes} WHERE `Db` = ? AND `User` = ?").as_str())
|
||||
.bind(p.select_priv.map(change_to_yn))
|
||||
.bind(p.insert_priv.map(change_to_yn))
|
||||
.bind(p.update_priv.map(change_to_yn))
|
||||
.bind(p.delete_priv.map(change_to_yn))
|
||||
.bind(p.create_priv.map(change_to_yn))
|
||||
.bind(p.drop_priv.map(change_to_yn))
|
||||
.bind(p.alter_priv.map(change_to_yn))
|
||||
.bind(p.index_priv.map(change_to_yn))
|
||||
.bind(p.create_tmp_table_priv.map(change_to_yn))
|
||||
.bind(p.lock_tables_priv.map(change_to_yn))
|
||||
.bind(p.references_priv.map(change_to_yn))
|
||||
.bind(p.db.to_string())
|
||||
.bind(p.user.to_string())
|
||||
.execute(connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
DatabasePrivilegesDiff::Deleted(p) => {
|
||||
sqlx::query("DELETE FROM `db` WHERE `Db` = ? AND `User` = ?")
|
||||
@@ -433,23 +435,37 @@ pub async fn apply_privilege_diffs(
|
||||
continue;
|
||||
}
|
||||
|
||||
if !unsafe_database_exists(diff.get_database_name(), connection)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
results.insert(
|
||||
key,
|
||||
Err(ModifyDatabasePrivilegesError::DatabaseDoesNotExist),
|
||||
);
|
||||
continue;
|
||||
match unsafe_database_exists(diff.get_database_name(), connection).await {
|
||||
Ok(false) => {
|
||||
results.insert(
|
||||
key,
|
||||
Err(ModifyDatabasePrivilegesError::DatabaseDoesNotExist),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
results.insert(
|
||||
key,
|
||||
Err(ModifyDatabasePrivilegesError::MySqlError(e.to_string())),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Ok(true) => {}
|
||||
}
|
||||
|
||||
if !unsafe_user_exists(diff.get_user_name(), connection)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
results.insert(key, Err(ModifyDatabasePrivilegesError::UserDoesNotExist));
|
||||
continue;
|
||||
match unsafe_user_exists(diff.get_user_name(), connection).await {
|
||||
Ok(false) => {
|
||||
results.insert(key, Err(ModifyDatabasePrivilegesError::UserDoesNotExist));
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
results.insert(
|
||||
key,
|
||||
Err(ModifyDatabasePrivilegesError::MySqlError(e.to_string())),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Ok(true) => {}
|
||||
}
|
||||
|
||||
if let Err(err) = validate_diff(&diff, connection).await {
|
||||
|
||||
@@ -34,13 +34,13 @@ pub(super) async fn unsafe_user_exists(
|
||||
connection: &mut MySqlConnection,
|
||||
) -> Result<bool, sqlx::Error> {
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
r"
|
||||
SELECT EXISTS(
|
||||
SELECT 1
|
||||
FROM `mysql`.`user`
|
||||
WHERE `User` = ?
|
||||
)
|
||||
"#,
|
||||
",
|
||||
)
|
||||
.bind(db_user)
|
||||
.fetch_one(connection)
|
||||
@@ -62,15 +62,15 @@ pub async fn complete_user_name(
|
||||
group_denylist: &GroupDenylist,
|
||||
) -> Vec<MySQLUser> {
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
r"
|
||||
SELECT `User` AS `user`
|
||||
FROM `mysql`.`user`
|
||||
WHERE `User` REGEXP ?
|
||||
AND `User` LIKE ?
|
||||
"#,
|
||||
",
|
||||
)
|
||||
.bind(create_user_group_matching_regex(unix_user, group_denylist))
|
||||
.bind(format!("{}%", user_prefix))
|
||||
.bind(format!("{user_prefix}%"))
|
||||
.fetch_all(connection)
|
||||
.await;
|
||||
|
||||
@@ -236,12 +236,12 @@ const DATABASE_USER_LOCK_STATUS_QUERY_MARIADB: &str = r#"
|
||||
AND `Host` = '%'
|
||||
"#;
|
||||
|
||||
const DATABASE_USER_LOCK_STATUS_QUERY_MYSQL: &str = r#"
|
||||
const DATABASE_USER_LOCK_STATUS_QUERY_MYSQL: &str = r"
|
||||
SELECT `mysql`.`user`.`account_locked` = 'Y'
|
||||
FROM `mysql`.`user`
|
||||
WHERE `User` = ?
|
||||
AND `Host` = '%'
|
||||
"#;
|
||||
";
|
||||
|
||||
// NOTE: this function is unsafe because it does no input validation.
|
||||
async fn database_user_is_locked_unsafe(
|
||||
@@ -430,14 +430,14 @@ JOIN `global_priv` ON
|
||||
AND `user`.`Host` = `global_priv`.`Host`
|
||||
"#;
|
||||
|
||||
const DB_USER_SELECT_STATEMENT_MYSQL: &str = r#"
|
||||
const DB_USER_SELECT_STATEMENT_MYSQL: &str = r"
|
||||
SELECT
|
||||
`user`.`User`,
|
||||
`user`.`Host`,
|
||||
`user`.`authentication_string` != '' AS `has_password`,
|
||||
`user`.`account_locked` = 'Y' AS `account_locked`
|
||||
FROM `user`
|
||||
"#;
|
||||
";
|
||||
|
||||
pub async fn list_database_users(
|
||||
db_users: Vec<MySQLUser>,
|
||||
@@ -472,8 +472,10 @@ pub async fn list_database_users(
|
||||
tracing::error!("Failed to list database user '{}': {:?}", &db_user, err);
|
||||
}
|
||||
|
||||
if let Ok(Some(user)) = result.as_mut() {
|
||||
append_databases_where_user_has_privileges(user, &mut *connection).await;
|
||||
if let Ok(Some(user)) = result.as_mut()
|
||||
&& let Err(err) = set_databases_where_user_has_privileges(user, &mut *connection).await
|
||||
{
|
||||
result = Err(err);
|
||||
}
|
||||
|
||||
match result {
|
||||
@@ -510,27 +512,33 @@ pub async fn list_all_database_users_for_unix_user(
|
||||
|
||||
if let Ok(users) = result.as_mut() {
|
||||
for user in users {
|
||||
append_databases_where_user_has_privileges(user, &mut *connection).await;
|
||||
if let Err(mysql_error) =
|
||||
set_databases_where_user_has_privileges(user, &mut *connection).await
|
||||
{
|
||||
return Err(ListAllUsersError::MySqlError(mysql_error.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn append_databases_where_user_has_privileges(
|
||||
/// This function sets the `databases` field of the given `DatabaseUser`
|
||||
/// where the user has any privileges.
|
||||
pub async fn set_databases_where_user_has_privileges(
|
||||
db_user: &mut DatabaseUser,
|
||||
connection: &mut MySqlConnection,
|
||||
) {
|
||||
) -> Result<(), sqlx::Error> {
|
||||
let database_list = sqlx::query(
|
||||
formatdoc!(
|
||||
r#"
|
||||
r"
|
||||
SELECT `Db` AS `database`
|
||||
FROM `db`
|
||||
WHERE `User` = ? AND ({})
|
||||
"#,
|
||||
",
|
||||
DATABASE_PRIVILEGE_FIELDS
|
||||
.iter()
|
||||
.map(|field| format!("`{}` = 'Y'", field))
|
||||
.map(|field| format!("`{field}` = 'Y'"))
|
||||
.join(" OR "),
|
||||
)
|
||||
.as_str(),
|
||||
@@ -547,11 +555,11 @@ pub async fn append_databases_where_user_has_privileges(
|
||||
);
|
||||
}
|
||||
|
||||
db_user.databases = database_list
|
||||
.map(|rows| {
|
||||
rows.into_iter()
|
||||
.map(|row| try_get_with_binary_fallback(&row, "database").unwrap())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
db_user.databases = database_list.and_then(|rows| {
|
||||
rows.into_iter()
|
||||
.map(|row| try_get_with_binary_fallback(&row, "database"))
|
||||
.collect::<Result<Vec<String>, sqlx::Error>>()
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -71,21 +71,19 @@ impl Supervisor {
|
||||
let config = ServerConfig::read_config_from_path(&config_path)
|
||||
.context("Failed to read server configuration")?;
|
||||
|
||||
let group_deny_list = match &config.authorization.group_denylist_file {
|
||||
Some(denylist_path) => {
|
||||
let denylist = read_and_parse_group_denylist(denylist_path)
|
||||
.context("Failed to read group denylist file")?;
|
||||
tracing::debug!(
|
||||
"Loaded group denylist with {} entries from {:?}",
|
||||
denylist.len(),
|
||||
denylist_path
|
||||
);
|
||||
Arc::new(RwLock::new(denylist))
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("No group denylist file specified, proceeding without a denylist");
|
||||
Arc::new(RwLock::new(GroupDenylist::new()))
|
||||
}
|
||||
let group_deny_list = if let Some(denylist_path) = &config.authorization.group_denylist_file
|
||||
{
|
||||
let denylist = read_and_parse_group_denylist(denylist_path)
|
||||
.context("Failed to read group denylist file")?;
|
||||
tracing::debug!(
|
||||
"Loaded group denylist with {} entries from {:?}",
|
||||
denylist.len(),
|
||||
denylist_path
|
||||
);
|
||||
Arc::new(RwLock::new(denylist))
|
||||
} else {
|
||||
tracing::debug!("No group denylist file specified, proceeding without a denylist");
|
||||
Arc::new(RwLock::new(GroupDenylist::new()))
|
||||
};
|
||||
|
||||
let mut watchdog_duration = None;
|
||||
@@ -93,12 +91,13 @@ impl Supervisor {
|
||||
#[cfg(target_os = "linux")]
|
||||
let watchdog_task =
|
||||
if systemd_mode && sd_notify::watchdog_enabled(true, &mut watchdog_micro_seconds) {
|
||||
watchdog_duration = Some(Duration::from_micros(watchdog_micro_seconds));
|
||||
let watchdog_duration_ = Duration::from_micros(watchdog_micro_seconds);
|
||||
tracing::debug!(
|
||||
"Systemd watchdog enabled with {} millisecond interval",
|
||||
watchdog_micro_seconds.div_ceil(1000),
|
||||
);
|
||||
Some(spawn_watchdog_task(watchdog_duration.unwrap()))
|
||||
watchdog_duration = Some(watchdog_duration_);
|
||||
Some(spawn_watchdog_task(watchdog_duration_))
|
||||
} else {
|
||||
tracing::debug!("Systemd watchdog not enabled, skipping watchdog thread");
|
||||
None
|
||||
@@ -221,22 +220,20 @@ impl Supervisor {
|
||||
let mut config = self.config.clone().lock_owned().await;
|
||||
*config = new_config;
|
||||
|
||||
let group_deny_list = match &config.authorization.group_denylist_file {
|
||||
Some(denylist_path) => {
|
||||
let denylist = read_and_parse_group_denylist(denylist_path)
|
||||
.context("Failed to read group denylist file")?;
|
||||
let group_deny_list = if let Some(denylist_path) = &config.authorization.group_denylist_file
|
||||
{
|
||||
let denylist = read_and_parse_group_denylist(denylist_path)
|
||||
.context("Failed to read group denylist file")?;
|
||||
|
||||
tracing::debug!(
|
||||
"Loaded group denylist with {} entries from {:?}",
|
||||
denylist.len(),
|
||||
denylist_path
|
||||
);
|
||||
denylist
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("No group denylist file specified, proceeding without a denylist");
|
||||
GroupDenylist::new()
|
||||
}
|
||||
tracing::debug!(
|
||||
"Loaded group denylist with {} entries from {:?}",
|
||||
denylist.len(),
|
||||
denylist_path
|
||||
);
|
||||
denylist
|
||||
} else {
|
||||
tracing::debug!("No group denylist file specified, proceeding without a denylist");
|
||||
GroupDenylist::new()
|
||||
};
|
||||
let mut group_deny_list_lock = self.group_deny_list.write().await;
|
||||
*group_deny_list_lock = group_deny_list;
|
||||
@@ -387,7 +384,7 @@ impl Supervisor {
|
||||
}
|
||||
}
|
||||
|
||||
_ = self.shutdown_cancel_token.cancelled() => {
|
||||
() = self.shutdown_cancel_token.cancelled() => {
|
||||
tracing::info!("Shutting down server");
|
||||
self.shutdown().await?;
|
||||
break;
|
||||
@@ -427,7 +424,7 @@ fn spawn_status_notifier_task(task_tracker: TaskTracker) -> JoinHandle<()> {
|
||||
let count = task_tracker.len();
|
||||
|
||||
let message = if count > 0 {
|
||||
format!("Handling {} connections", count)
|
||||
format!("Handling {count} connections")
|
||||
} else {
|
||||
"Waiting for connections".to_string()
|
||||
};
|
||||
@@ -453,7 +450,7 @@ async fn create_unix_listener_with_socket_path(
|
||||
tracing::info!("Listening on socket {:?}", socket_path);
|
||||
|
||||
match fs::remove_file(socket_path.as_path()) {
|
||||
Ok(_) => {}
|
||||
Ok(()) => {}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
@@ -470,7 +467,7 @@ async fn create_unix_listener_with_systemd_socket() -> anyhow::Result<TokioUnixL
|
||||
.next()
|
||||
.context("No file descriptors received from systemd")?;
|
||||
|
||||
debug_assert!(fd == 3, "Unexpected file descriptor from systemd: {}", fd);
|
||||
debug_assert!(fd == 3, "Unexpected file descriptor from systemd: {fd}");
|
||||
|
||||
tracing::debug!(
|
||||
"Received file descriptor from systemd with id: '{}', assuming socket",
|
||||
|
||||
Reference in New Issue
Block a user