pub mod methods; use dotenv::dotenv; use ldap3::{LdapConn, Scope, SearchEntry}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::{Error, Pool, Sqlite}; use std::env; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use tide::prelude::*; #[derive(Debug, Deserialize, Serialize, sqlx::FromRow)] pub struct AccountsPending { user: String, mail: String, name_first: String, name_second: String, auth_code: String, // will only last for a few hours expiry: i64, } #[derive(Debug, Deserialize, Serialize, sqlx::FromRow)] pub struct Accounts { user: String, uid_number: i64, discord: Option, enabled: bool, } pub async fn db_init(config: &Config) -> Result, Error> { let database = &config.database; let pool = SqlitePoolOptions::new() .max_connections(5) .connect_with(SqliteConnectOptions::from_str(&format!("sqlite://{}", database))?.create_if_missing(true)) .await?; sqlx::query( "CREATE TABLE IF NOT EXISTS accounts_pending ( user text primary key, mail text not null, name_first text not null, name_second text not null, auth_code text not null, expiry integer not null )", ) .execute(&pool) .await?; // this is for active use sqlx::query( "CREATE TABLE IF NOT EXISTS accounts ( user text primary key, uid_number integer not null, discord text, enabled integer not null )", ) .execute(&pool) .await?; sqlx::query("CREATE INDEX IF NOT EXISTS index_uid_number ON accounts (uid_number)") .execute(&pool) .await?; update_accounts(&pool, config).await; Ok(pool) } pub fn get_now() -> i64 { if let Ok(x) = SystemTime::now().duration_since(UNIX_EPOCH) { x.as_secs() as i64 } else { 0 } } #[derive(Clone)] pub struct State { pub db: Pool, pub config: Config, } #[derive(Debug, Clone)] pub struct Config { ldap_host: String, ldap_admin: String, ldap_admin_pw: String, pub database: String, pub host_port: String, } pub fn get_config() -> Config { dotenv().ok(); // reasonable defaults let mut config = Config { ldap_host: "".to_string(), ldap_admin: "".to_string(), ldap_admin_pw: "".to_string(), database: "database.db".to_string(), host_port: "127.0.0.1:8087".to_string(), }; if let Ok(x) = env::var("LDAP_HOST") { config.ldap_host = x.trim().to_string(); } if let Ok(x) = env::var("LDAP_ADMIN") { config.ldap_admin = x.trim().to_string(); } if let Ok(x) = env::var("LDAP_ADMIN_PW") { config.ldap_admin_pw = x.trim().to_string(); } if let Ok(x) = env::var("DATABASE") { config.database = x.trim().to_string(); } if let Ok(x) = env::var("HOST_PORT") { config.host_port = x.trim().to_string(); } config } async fn update_accounts(pool: &Pool, config: &Config) { let mut ldap = LdapConn::new(&config.ldap_host).unwrap(); ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw).unwrap().success().unwrap(); if let Ok(x) = ldap.search("ou=users,dc=skynet,dc=ie", Scope::OneLevel, "(objectClass=*)", vec!["uid", "uidNumber", "skDiscord", "skMemberOf"]) { if let Ok((rs, _res)) = x.success() { for entry in rs { let tmp = SearchEntry::construct(entry); let mut tmp_account = Accounts { user: "".to_string(), uid_number: 0, discord: None, enabled: false, }; // pull out the required info if tmp.attrs.contains_key("uid") && !tmp.attrs["uid"].is_empty() { tmp_account.user = tmp.attrs["uid"][0].clone(); } if tmp.attrs.contains_key("uidNumber") && !tmp.attrs["uidNumber"].is_empty() { tmp_account.uid_number = tmp.attrs["uidNumber"][0].clone().parse().unwrap_or(0); } if tmp.attrs.contains_key("skDiscord") && !tmp.attrs["skDiscord"].is_empty() { tmp_account.user = tmp.attrs["skDiscord"][0].clone(); } if tmp.attrs.contains_key("skMemberOf") && !tmp.attrs["skMemberOf"].is_empty() && tmp.attrs["skMemberOf"].contains(&String::from("cn=skynet-users,ou=groups,dc=skynet,dc=ie")) { tmp_account.enabled = true; } if !tmp_account.user.is_empty() { sqlx::query_as::<_, Accounts>( " INSERT OR REPLACE INTO accounts (user, uid_number, discord, enabled) VALUES (?1, ?2, ?3, ?4) ", ) .bind(&tmp_account.user) .bind(tmp_account.uid_number) .bind(&tmp_account.discord) .bind(tmp_account.enabled) .fetch_optional(pool) .await .ok(); } } } } // done with ldap ldap.unbind().unwrap(); }