use crate::config::{Config, Role, User as UserInConfig};
use crate::connection::{DbConnection, User};
use anyhow::Result;
use ascii_table::AsciiTable;
use log::error;
use log::info;
pub fn apply(config: &Config, dryrun: bool) -> Result<()> {
info!("Applying configuration:\n{}", config);
let mut conn = DbConnection::new(config);
let users_in_db = conn.get_users()?;
let users_in_config = config.users.clone();
apply_users(&mut conn, &users_in_db, &users_in_config, dryrun)?;
apply_database_privileges(&mut conn, &config, dryrun)?;
apply_schema_privileges(&mut conn, &config, dryrun)?;
apply_table_privileges(&mut conn, &config, dryrun)?;
Ok(())
}
fn apply_users(
conn: &mut DbConnection,
users_in_db: &[User],
users_in_config: &[UserInConfig],
dryrun: bool,
) -> Result<()> {
let mut summary = vec![vec!["User".to_string(), "Action".to_string()]];
summary.push(vec!["---".to_string(), "---".to_string()]);
for user in users_in_config {
let user_in_db = users_in_db.iter().find(|&u| u.name == user.name);
match user_in_db {
Some(user_in_db) => {
summary.push(vec![user_in_db.name.clone(), "no action (existing)".to_string()]);
}
None => {
let new_user = User::new(user.get_name(), false, false, user.get_password());
if !dryrun {
conn.create_user(&new_user);
info!("User {} created", new_user.name);
} else {
info!("User {} would be created", new_user.name);
}
summary.push(vec![user.name.clone(), "created".to_string()]);
}
}
}
for user in users_in_db {
if !users_in_config.iter().any(|u| u.name == user.name) {
summary.push(vec![
user.name.clone(),
"no action (not in config)".to_string(),
]);
}
}
print_summary(summary);
Ok(())
}
pub fn apply_database_privileges(
conn: &mut DbConnection,
config: &Config,
dryrun: bool,
) -> Result<()> {
let mut summary = vec![vec![
"User".to_string(),
"Database Privilege".to_string(),
"Action".to_string(),
]];
summary.push(vec![
"---".to_string(),
"---".to_string(),
"---".to_string(),
]);
let user_privileges_on_db = conn.get_user_database_privileges()?;
for user in &config.users {
let user_roles_in_config: Vec<_> = user
.roles
.iter()
.map(|role_name| {
config
.roles
.iter()
.find(|&r| r.get_name() == role_name.to_string())
.unwrap()
})
.collect();
let privileges_on_db = user_privileges_on_db.iter().find(|&p| p.name == user.name);
for role in user_roles_in_config {
match role {
Role::Database(role) => {
let sql = role.to_sql_grant(user.name.clone());
if !dryrun {
conn.query(&sql, &[]).unwrap_or_else(|e| {
error!("failed to run sql:\n{}", sql);
panic!("{}", e);
});
info!("Granting: {}", sql);
} else {
info!("Granting: {}", sql);
}
let action = if privileges_on_db.is_none() {
"granted"
} else {
"updated"
};
summary.push(vec![
user.name.clone(),
format!(
"`{}` for database: {:?}",
role.name.clone(),
role.databases.clone()
),
action.to_string(),
]);
}
_ => {}
}
}
}
print_summary(summary);
Ok(())
}
pub fn apply_schema_privileges(
conn: &mut DbConnection,
config: &Config,
dryrun: bool,
) -> Result<()> {
let mut summary = vec![vec![
"User".to_string(),
"Schema Privileges".to_string(),
"Action".to_string(),
]];
summary.push(vec![
"---".to_string(),
"---".to_string(),
"---".to_string(),
]);
let user_privileges_on_db = conn.get_user_schema_privileges()?;
for user in &config.users {
let user_roles_in_config: Vec<_> = user
.roles
.iter()
.map(|role_name| {
config
.roles
.iter()
.find(|&r| r.get_name() == role_name.to_string())
.unwrap()
})
.collect();
let privileges_on_db = user_privileges_on_db.iter().find(|&p| p.name == user.name);
for role in user_roles_in_config {
match role {
Role::Schema(role) => {
let sql = role.to_sql_grant(user.name.clone());
if !dryrun {
conn.query(&sql, &[]).unwrap_or_else(|e| {
error!("failed to run sql:\n{}", sql);
panic!("{}", e);
});
info!("Granting: {}", sql);
} else {
info!("Granting: {}", sql);
}
let action = if privileges_on_db.is_none() {
"granted"
} else {
"updated"
};
summary.push(vec![
user.name.clone(),
format!(
"`{}` for schema: {:?}",
role.name.clone(),
role.schemas.clone()
),
action.to_string(),
]);
}
_ => {}
}
}
}
print_summary(summary);
Ok(())
}
pub fn apply_table_privileges(
conn: &mut DbConnection,
config: &Config,
dryrun: bool,
) -> Result<()> {
let mut summary = vec![vec![
"User".to_string(),
"Table Privileges".to_string(),
"Action".to_string(),
]];
summary.push(vec![
"---".to_string(),
"---".to_string(),
"---".to_string(),
]);
let user_privileges_on_db = conn.get_user_table_privileges()?;
for user in &config.users {
let user_roles_in_config: Vec<_> = user
.roles
.iter()
.map(|role_name| {
config
.roles
.iter()
.find(|&r| r.get_name() == role_name.to_string())
.unwrap()
})
.collect();
let privileges_on_db = user_privileges_on_db.iter().find(|&p| p.name == user.name);
for role in user_roles_in_config {
match role {
Role::Table(role) => {
let sql = role.to_sql_grant(user.name.clone());
if !dryrun {
conn.query(&sql, &[]).unwrap_or_else(|e| {
error!("failed to run sql:\n{}", sql);
panic!("{}", e);
});
info!("Granting: {}", sql);
} else {
info!("Granting: {}", sql);
}
let action = if privileges_on_db.is_none() {
"granted"
} else {
"updated"
};
summary.push(vec![
user.name.clone(),
format!(
"`{}` for table: {:?}",
role.name.clone(),
role.tables.clone()
),
action.to_string(),
]);
}
_ => {}
}
}
}
print_summary(summary);
Ok(())
}
fn print_summary(summary: Vec<Vec<String>>) {
let ascii_table = AsciiTable::default();
info!("Summary:\n{}", ascii_table.format(summary));
}