ldap_backend/src/bin/update_groups.rs

188 lines
4.9 KiB
Rust

use skynet_ldap_backend::{db_init, get_config, get_now_iso, get_wolves, update_group, Accounts, Config};
use sqlx::{Pool, Sqlite};
use std::{collections::HashSet, env, error::Error};
#[async_std::main]
async fn main() -> tide::Result<()> {
let config = get_config();
update(&config).await?;
Ok(())
}
async fn update(config: &Config) -> tide::Result<()> {
let db = db_init(config).await.unwrap();
// default user to ensure group is never empty
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") {
for user in x.split(',').collect::<Vec<&str>>() {
users_tmp.insert(user.to_string());
}
}
// pull from wolves csv
for user in from_csv(config).await.unwrap_or_default() {
users_tmp.insert(user);
}
if let Ok(x) = env::var("USERS_ADMIN") {
// admins automatically get added as users
for user in x.split(',').collect::<Vec<&str>>() {
admins_tmp.insert(user.to_string());
users_tmp.insert(user.to_string());
}
}
// read from teh env
if let Ok(x) = env::var("USERS_COMMITTEE") {
// committee automatically get added as users
for user in x.split(',').collect::<Vec<&str>>() {
committee_tmp.insert(user.to_string());
users_tmp.insert(user.to_string());
}
}
// sorting makes it easier/faster
if let Ok(x) = env::var("USERS_BANNED") {
for user in x.split(',').collect::<Vec<&str>>() {
users_tmp.remove(user);
}
}
let AccountsSecure {
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-admins", &admins, true).await?;
update_group(config, "skynet-committee", &committee, true).await?;
Ok(())
}
async fn from_csv(config: &Config) -> Result<HashSet<String>, Box<dyn Error>> {
let db = db_init(config).await.unwrap();
let mut uids = HashSet::new();
for record in get_wolves(&db).await {
// only import users if it is actually active.
if record.expiry < get_now_iso(true) {
continue;
}
if let Some(uid) = account_mail_get_uid(&db, &record.email).await {
uids.insert(uid);
} else if let Some(uid) = account_id_get_uid(&db, &record.id_student).await {
uids.insert(uid);
}
}
Ok(uids)
}
async fn account_mail_get_uid(db: &Pool<Sqlite>, mail: &str) -> Option<String> {
match sqlx::query_as::<_, Accounts>(
r#"
SELECT user
FROM accounts
WHERE mail == ?
"#,
)
.bind(mail)
.fetch_all(db)
.await
{
Ok(res) => {
if res.is_empty() {
None
} else {
Some(res[0].user.to_owned())
}
}
Err(_) => None,
}
}
async fn account_id_get_uid(db: &Pool<Sqlite>, id: &str) -> Option<String> {
match sqlx::query_as::<_, Accounts>(
r#"
SELECT user
FROM accounts
WHERE student_id == ?
"#,
)
.bind(id)
.fetch_all(db)
.await
{
Ok(res) => {
if res.is_empty() {
None
} else {
Some(res[0].student_id.to_owned())
}
}
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,
}
}