Compare commits
2 Commits
54a38255e6
...
168f832aec
Author | SHA1 | Date |
---|---|---|
Oystein Kristoffer Tveit | 168f832aec | |
Oystein Kristoffer Tveit | f957bbd9da |
|
@ -290,14 +290,14 @@ async fn show_users(
|
|||
"User",
|
||||
"Password is set",
|
||||
"Locked",
|
||||
// "Databases where user has privileges"
|
||||
"Databases where user has privileges"
|
||||
]);
|
||||
for user in users {
|
||||
table.add_row(row![
|
||||
user.user,
|
||||
user.has_password,
|
||||
user.is_locked,
|
||||
// user.databases.join("\n")
|
||||
user.databases.join("\n")
|
||||
]);
|
||||
}
|
||||
table.printstd();
|
||||
|
|
|
@ -160,7 +160,9 @@ pub fn generate_editor_content_from_privilege_data(
|
|||
database_name: Option<&str>,
|
||||
) -> String {
|
||||
let example_user = format!("{}_user", unix_user);
|
||||
let example_db = database_name.unwrap_or(&format!("{}_db", unix_user)).to_string();
|
||||
let example_db = database_name
|
||||
.unwrap_or(&format!("{}_db", unix_user))
|
||||
.to_string();
|
||||
|
||||
// NOTE: `.max()`` fails when the iterator is empty.
|
||||
// In this case, we know that the only fields in the
|
||||
|
|
|
@ -95,8 +95,7 @@ impl OwnerValidationError {
|
|||
.join("\n"),
|
||||
)
|
||||
.to_owned(),
|
||||
|
||||
_ => format!(
|
||||
OwnerValidationError::StringEmpty => format!(
|
||||
"'{}' is not a valid {} name.",
|
||||
name,
|
||||
db_or_user.lowercased()
|
||||
|
@ -113,12 +112,6 @@ pub enum OwnerValidationError {
|
|||
|
||||
// The name is empty, which is invalid
|
||||
StringEmpty,
|
||||
|
||||
// The name is in the format "_<postfix>", which is invalid
|
||||
MissingPrefix,
|
||||
|
||||
// The name is in the format "<prefix>_", which is invalid
|
||||
MissingPostfix,
|
||||
}
|
||||
|
||||
pub type CreateDatabasesOutput = BTreeMap<String, Result<(), CreateDatabaseError>>;
|
||||
|
|
|
@ -43,18 +43,14 @@ pub fn validate_ownership_by_prefixes(
|
|||
return Err(OwnerValidationError::StringEmpty);
|
||||
}
|
||||
|
||||
if name.starts_with('_') {
|
||||
return Err(OwnerValidationError::MissingPrefix);
|
||||
}
|
||||
|
||||
let (prefix, _) = match name.split_once('_') {
|
||||
Some(pair) => pair,
|
||||
None => return Err(OwnerValidationError::MissingPostfix),
|
||||
};
|
||||
|
||||
if !prefixes.iter().any(|g| g == prefix) {
|
||||
if prefixes
|
||||
.iter()
|
||||
.filter(|p| name.starts_with(*p))
|
||||
.collect::<Vec<_>>()
|
||||
.is_empty()
|
||||
{
|
||||
return Err(OwnerValidationError::NoMatch);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -115,24 +111,6 @@ mod tests {
|
|||
Err(OwnerValidationError::StringEmpty)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
validate_ownership_by_prefixes("user", &prefixes),
|
||||
Err(OwnerValidationError::MissingPostfix)
|
||||
);
|
||||
assert_eq!(
|
||||
validate_ownership_by_prefixes("something", &prefixes),
|
||||
Err(OwnerValidationError::MissingPostfix)
|
||||
);
|
||||
assert_eq!(
|
||||
validate_ownership_by_prefixes("user-testdb", &prefixes),
|
||||
Err(OwnerValidationError::MissingPostfix)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
validate_ownership_by_prefixes("_testdb", &prefixes),
|
||||
Err(OwnerValidationError::MissingPrefix)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
validate_ownership_by_prefixes("user_testdb", &prefixes),
|
||||
Ok(())
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use itertools::Itertools;
|
||||
use std::collections::BTreeMap;
|
||||
use indoc::formatdoc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -20,6 +22,8 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::database_privilege_operations::DATABASE_PRIVILEGE_FIELDS;
|
||||
|
||||
// NOTE: this function is unsafe because it does no input validation.
|
||||
async fn unsafe_user_exists(
|
||||
db_user: &str,
|
||||
|
@ -309,6 +313,9 @@ pub struct DatabaseUser {
|
|||
|
||||
#[sqlx(rename = "is_locked")]
|
||||
pub is_locked: bool,
|
||||
|
||||
#[sqlx(skip)]
|
||||
pub databases: Vec<String>,
|
||||
}
|
||||
|
||||
const DB_USER_SELECT_STATEMENT: &str = r#"
|
||||
|
@ -344,13 +351,17 @@ pub async fn list_database_users(
|
|||
continue;
|
||||
}
|
||||
|
||||
let result = sqlx::query_as::<_, DatabaseUser>(
|
||||
let mut result = sqlx::query_as::<_, DatabaseUser>(
|
||||
&(DB_USER_SELECT_STATEMENT.to_string() + "WHERE `mysql`.`user`.`User` = ?"),
|
||||
)
|
||||
.bind(&db_user)
|
||||
.fetch_optional(&mut *connection)
|
||||
.await;
|
||||
|
||||
if let Ok(Some(user)) = result.as_mut() {
|
||||
append_databases_where_user_has_privileges(user, &mut *connection).await;
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(Some(user)) => results.insert(db_user, Ok(user)),
|
||||
Ok(None) => results.insert(db_user, Err(ListUsersError::UserDoesNotExist)),
|
||||
|
@ -365,11 +376,50 @@ pub async fn list_all_database_users_for_unix_user(
|
|||
unix_user: &UnixUser,
|
||||
connection: &mut MySqlConnection,
|
||||
) -> ListAllUsersOutput {
|
||||
sqlx::query_as::<_, DatabaseUser>(
|
||||
let mut result = sqlx::query_as::<_, DatabaseUser>(
|
||||
&(DB_USER_SELECT_STATEMENT.to_string() + "WHERE `mysql`.`user`.`User` REGEXP ?"),
|
||||
)
|
||||
.bind(create_user_group_matching_regex(unix_user))
|
||||
.fetch_all(connection)
|
||||
.fetch_all(&mut *connection)
|
||||
.await
|
||||
.map_err(|err| ListAllUsersError::MySqlError(err.to_string()))
|
||||
.map_err(|err| ListAllUsersError::MySqlError(err.to_string()));
|
||||
|
||||
if let Ok(users) = result.as_mut() {
|
||||
for user in users {
|
||||
append_databases_where_user_has_privileges(user, &mut *connection).await;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn append_databases_where_user_has_privileges(
|
||||
database_user: &mut DatabaseUser,
|
||||
connection: &mut MySqlConnection,
|
||||
) {
|
||||
let database_list = sqlx::query(
|
||||
formatdoc!(
|
||||
r#"
|
||||
SELECT `db` AS `database`
|
||||
FROM `db`
|
||||
WHERE `user` = ? AND ({})
|
||||
"#,
|
||||
DATABASE_PRIVILEGE_FIELDS
|
||||
.iter()
|
||||
.map(|field| format!("`{}` = 'Y'", field))
|
||||
.join(" OR "),
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.bind(database_user.user.clone())
|
||||
.fetch_all(&mut *connection)
|
||||
.await;
|
||||
|
||||
database_user.databases = database_list
|
||||
.map(|rows| {
|
||||
rows.into_iter()
|
||||
.map(|row| row.get::<String, _>("database"))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue