Merge branch '#7_secure_password' into 'main'

Ensure users have a secure password to be added to groups

Closes #7

See merge request compsoc1/skynet/ldap/backend!4
This commit is contained in:
Brendan Golden 2023-08-02 13:57:23 +00:00
commit 91ee2a246e

View file

@ -1,4 +1,3 @@
use dotenvy::dotenv;
use ldap3::{LdapConn, Mod}; use ldap3::{LdapConn, Mod};
use skynet_ldap_backend::{db_init, get_config, get_now_iso, read_csv, Accounts, Config}; use skynet_ldap_backend::{db_init, get_config, get_now_iso, read_csv, Accounts, Config};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
@ -14,11 +13,12 @@ async fn main() -> tide::Result<()> {
} }
async fn update(config: &Config) -> tide::Result<()> { async fn update(config: &Config) -> tide::Result<()> {
dotenv().ok(); let db = db_init(config).await.unwrap();
let mut users_tmp = HashSet::new();
// default user to ensure group is never empty // default user to ensure group is never empty
users_tmp.insert(String::from("compsoc")); let mut users_tmp = HashSet::from([String::from("compsoc")]);
let mut admins_tmp = HashSet::from([String::from("compsoc")]);
let mut committee_tmp = HashSet::from([String::from("compsoc")]);
if let Ok(x) = env::var("USERS_LIFETIME") { if let Ok(x) = env::var("USERS_LIFETIME") {
for user in x.split(',').collect::<Vec<&str>>() { for user in x.split(',').collect::<Vec<&str>>() {
@ -32,22 +32,18 @@ async fn update(config: &Config) -> tide::Result<()> {
} }
if let Ok(x) = env::var("USERS_ADMIN") { if let Ok(x) = env::var("USERS_ADMIN") {
let users = x.split(',').collect::<Vec<&str>>();
update_group(config, "skynet-admins", &users, true).await?;
// admins automatically get added as users // admins automatically get added as users
for user in users { for user in x.split(',').collect::<Vec<&str>>() {
admins_tmp.insert(user.to_string());
users_tmp.insert(user.to_string()); users_tmp.insert(user.to_string());
} }
} }
// read from teh env // read from teh env
if let Ok(x) = env::var("USERS_COMMITTEE") { if let Ok(x) = env::var("USERS_COMMITTEE") {
let users = x.split(',').collect::<Vec<&str>>();
update_group(config, "skynet-committee", &users, true).await?;
// committee automatically get added as users // committee automatically get added as users
for user in users { for user in x.split(',').collect::<Vec<&str>>() {
committee_tmp.insert(user.to_string());
users_tmp.insert(user.to_string()); users_tmp.insert(user.to_string());
} }
} }
@ -59,10 +55,15 @@ async fn update(config: &Config) -> tide::Result<()> {
} }
} }
// easier to work with Strings above but easier to work with &str below let AccountsSecure {
let users: Vec<&str> = users_tmp.iter().map(|s| &**s).collect(); users,
admins,
committee,
} = get_secure(&db, &users_tmp, &admins_tmp, &committee_tmp).await;
update_group(config, "skynet-users", &users, true).await?; update_group(config, "skynet-users", &users, true).await?;
update_group(config, "skynet-admins", &admins, true).await?;
update_group(config, "skynet-committee", &committee, true).await?;
Ok(()) Ok(())
} }
@ -71,7 +72,7 @@ fn uid_to_dn(uid: &str) -> String {
format!("uid={},ou=users,dc=skynet,dc=ie", uid) format!("uid={},ou=users,dc=skynet,dc=ie", uid)
} }
async fn update_group(config: &Config, group: &str, users: &[&str], replace: bool) -> tide::Result<()> { async fn update_group(config: &Config, group: &str, users: &Vec<String>, replace: bool) -> tide::Result<()> {
if users.is_empty() { if users.is_empty() {
return Ok(()); return Ok(());
} }
@ -178,3 +179,56 @@ async fn account_id_get_uid(db: &Pool<Sqlite>, id: &str) -> Option<String> {
Err(_) => None, Err(_) => None,
} }
} }
struct AccountsSecure {
users: Vec<String>,
admins: Vec<String>,
committee: Vec<String>,
}
async fn get_secure(db: &Pool<Sqlite>, users: &HashSet<String>, admins: &HashSet<String>, committee: &HashSet<String>) -> AccountsSecure {
// to avoid searching for teh same thing again.
let mut cache = HashSet::new();
AccountsSecure {
users: get_secure_sub(db, users, &mut cache).await,
admins: get_secure_sub(db, admins, &mut cache).await,
committee: get_secure_sub(db, committee, &mut cache).await,
}
}
async fn get_secure_sub(db: &Pool<Sqlite>, group: &HashSet<String>, cache: &mut HashSet<String>) -> Vec<String> {
let mut tmp = vec![];
for user in group {
// check the cache first
let mut add = false;
if cache.get(user).is_some() {
add = true;
} else if is_secure(db, user).await {
cache.insert(user.to_string());
add = true;
}
if add {
tmp.push(user.clone());
}
}
tmp
}
async fn is_secure(db: &Pool<Sqlite>, user: &str) -> bool {
match sqlx::query_as::<_, Accounts>(
r#"
SELECT *
FROM accounts
WHERE user == ? AND secure == 1
"#,
)
.bind(user)
.fetch_all(db)
.await
{
Ok(res) => !res.is_empty(),
Err(_) => false,
}
}