diff --git a/src/lib.rs b/src/lib.rs index 48197c3..b54c601 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,9 +12,12 @@ use tide::prelude::*; pub struct AccountsPending { user: String, mail: String, - name_first: String, - name_second: String, + cn: String, + sn: String, auth_code: String, + + // action will be what to do with it + action: String, // will only last for a few hours expiry: i64, } @@ -38,8 +41,9 @@ pub async fn db_init(config: &Config) -> Result, Error> { "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, + cn text not null, + sn text not null, + action text not null, auth_code text not null, expiry integer not null )", diff --git a/src/methods/account_new.rs b/src/methods/account_new.rs index bec09d7..0a424cd 100644 --- a/src/methods/account_new.rs +++ b/src/methods/account_new.rs @@ -1,7 +1,8 @@ use crate::{get_now, Accounts, AccountsPending, State}; use ldap3::exop::PasswordModify; +use ldap3::result::ExopResult; use ldap3::{LdapConn, Scope}; -use sqlx::{Pool, Sqlite}; +use sqlx::{Error, Pool, Sqlite}; use std::collections::HashSet; use tide::prelude::{json, Deserialize}; use tide::Request; @@ -67,7 +68,7 @@ pub async fn post_new_account(mut req: Request) -> tide::Result { // setup the pool, going to need it for the rest of it let pool = &req.state().db; - db_pending_clear_expired(pool).await; + db_pending_clear_expired(pool).await?; // now check local if let Ok(results) = sqlx::query_as::<_, AccountsPending>( @@ -112,21 +113,22 @@ pub async fn post_new_account(mut req: Request) -> tide::Result { if not generate tuhe user and send email */ + let cn = format!("{} {}", name_first, name_second); let auth_code = create_random_string(50); - // 1 hour expiry let expiry = get_now() + (60 * 60); sqlx::query_as::<_, AccountsPending>( r#" - INSERT OR REPLACE INTO accounts_pending (user, mail, name_first, name_second, auth_code, expiry) - VALUES (?1, ?2, ?3, ?4, ?5, ?6) + INSERT OR REPLACE INTO accounts_pending (user, mail, cn, sn, action, auth_code, expiry) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) "#, ) .bind(&user) .bind(&mail) - .bind(&name_first) + .bind(&cn) .bind(&name_second) + .bind("account_new") .bind(&auth_code) .bind(expiry) .fetch_optional(pool) @@ -139,9 +141,9 @@ pub async fn post_new_account(mut req: Request) -> tide::Result { } // clear the db of expired ones before checking for username and validating inputs -async fn db_pending_clear_expired(pool: &Pool) { +async fn db_pending_clear_expired(pool: &Pool) -> Result, Error> { let now = get_now(); - if let Ok(results) = sqlx::query_as::<_, AccountsPending>( + sqlx::query_as::<_, AccountsPending>( r#" DELETE FROM accounts_pending @@ -151,9 +153,6 @@ async fn db_pending_clear_expired(pool: &Pool) { .bind(now) .fetch_all(pool) .await - { - println!("{:?}", results) - } } fn create_random_string(length: usize) -> String { @@ -164,17 +163,14 @@ fn create_random_string(length: usize) -> String { } #[derive(Debug, Deserialize)] -pub struct LdapNewUserVerify { +pub struct LdapUserVerify { auth_code: String, password: String, } /// Handles the verification that a user has access to the email pub async fn post_new_account_confirmation(mut req: Request) -> tide::Result { - let LdapNewUserVerify { - auth_code, - password, - } = req.body_json().await?; + let user_verify: LdapUserVerify = req.body_json().await?; let State { db, @@ -182,43 +178,96 @@ pub async fn post_new_account_confirmation(mut req: Request) -> tide::Res .. } = &req.state(); + // setup ldap connection + let mut ldap = LdapConn::new(&config.ldap_host)?; + ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?; + // make sure to clear out the expired ones first - db_pending_clear_expired(db).await; + db_pending_clear_expired(db).await?; // search db for auth_code - let results = sqlx::query_as::<_, AccountsPending>( - r#" - SELECT * - FROM accounts_pending - WHERE auth_code == ? - "#, - ) - .bind(&auth_code) - .fetch_all(db) - .await - .unwrap_or(vec![]); + let results = account_verification_find_pending(db, "account_new", &user_verify.auth_code).await; if results.is_empty() { return Ok(json!({"result": "error"}).into()); } - let mut ldap = LdapConn::new(&config.ldap_host)?; + let user_details = &results[0]; + let uid_number = get_max_uid_number(db).await; - // need to bind as admin - ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?; + // create teh new user account in ldap + account_verification_new_account(&mut ldap, user_details, uid_number).await?; + // now to properly set teh password + account_verification_reset_password_admin(&mut ldap, &user_details.user, &user_verify.password)?; + + // done with ldap + ldap.unbind()?; + + // delete from tmp db + account_verification_clear_pending(db, &user_verify.auth_code).await?; + + // add new users to teh local database + account_verification_add_local(db, &user_details.user, uid_number).await?; + + // frontend tells user that initial password ahs been sent to tehm + Ok(json!({"result": "success"}).into()) +} + +fn get_sk_created() -> String { + use chrono::Utc; + let now = Utc::now(); + + format!("{}", now.format("%Y%m%d%H%M%SZ")) +} + +async fn get_max_uid_number(db: &Pool) -> i64 { + if let Ok(results) = sqlx::query_as::<_, Accounts>( + r#" + SELECT * + FROM accounts + ORDER BY uid_number DESC + LIMIT 1 + "#, + ) + .fetch_all(db) + .await + { + if !results.is_empty() { + return results[0].uid_number + 1; + } + } + + 9999 +} + +async fn account_verification_find_pending(db: &Pool, action: &str, auth_code: &str) -> Vec { + sqlx::query_as::<_, AccountsPending>( + r#" + SELECT * + FROM accounts_pending + WHERE auth_code == ? AND action == ? + "#, + ) + .bind(auth_code) + .bind(action) + .fetch_all(db) + .await + .unwrap_or(vec![]) +} + +async fn account_verification_new_account(ldap: &mut LdapConn, user_details: &AccountsPending, uid_number: i64) -> Result<(), ldap3::LdapError> { let AccountsPending { user, mail, - name_first, - name_second, + cn, + sn, .. - } = &results[0]; + } = user_details; + let dn = format!("uid={},ou=users,dc=skynet,dc=ie", user); - let uid_number = get_max_uid_number(db).await; let home_directory = format!("/home/{}", user); let password_tmp = create_random_string(50); - let cn = format!("{} {}", name_first, name_second); let labeled_uri = format!("ldap:///ou=groups,dc=skynet,dc=ie??sub?(&(objectclass=posixgroup)(memberuid={}))", user); let sk_mail = format!("{}@skynet.ie", user); let sk_created = get_sk_created(); @@ -240,7 +289,7 @@ pub async fn post_new_account_confirmation(mut req: Request) -> tide::Res ("userpassword", HashSet::from([password_tmp.as_str()])), // inetOrgPerson ("mail", HashSet::from([mail.as_str()])), - ("sn", HashSet::from([name_second.as_str()])), + ("sn", HashSet::from([sn.as_str()])), // skPerson ("labeledURI", HashSet::from([labeled_uri.as_str()])), ("skMail", HashSet::from([sk_mail.as_str()])), @@ -251,32 +300,35 @@ pub async fn post_new_account_confirmation(mut req: Request) -> tide::Res )? .success()?; + Ok(()) +} + +fn account_verification_reset_password_admin(ldap: &mut LdapConn, user: &str, pass: &str) -> Result { + let dn = format!("uid={},ou=users,dc=skynet,dc=ie", user); + // now to properly set teh password let tmp = PasswordModify { user_id: Some(&dn), - old_pass: Some(&password_tmp), - new_pass: Some(&password), + old_pass: None, + new_pass: Some(pass), }; - ldap.extended(tmp)?.success()?; - // done with ldap - ldap.unbind()?; + ldap.extended(tmp) +} - // delete from tmp db - if let Ok(results) = sqlx::query_as::<_, AccountsPending>( +async fn account_verification_clear_pending(db: &Pool, auth_code: &str) -> Result, Error> { + sqlx::query_as::<_, AccountsPending>( r#" DELETE FROM accounts_pending WHERE auth_code == ? "#, ) - .bind(&auth_code) + .bind(auth_code) .fetch_all(db) .await - { - println!("{:?}", results) - } +} - // add new users to teh local database +async fn account_verification_add_local(db: &Pool, user: &str, uid_number: i64) -> Result, Error> { sqlx::query_as::<_, Accounts>( " INSERT OR REPLACE INTO accounts (user, uid_number, enabled) @@ -288,35 +340,4 @@ pub async fn post_new_account_confirmation(mut req: Request) -> tide::Res .bind(false) .fetch_optional(db) .await - .ok(); - - // frontend tells user that initial password ahs been sent to tehm - Ok(json!({"result": "success"}).into()) -} - -fn get_sk_created() -> String { - use chrono::Utc; - let now = Utc::now(); - - format!("{}", now.format("%Y%m%d%H%M%SZ")) -} - -pub async fn get_max_uid_number(db: &Pool) -> i64 { - if let Ok(results) = sqlx::query_as::<_, Accounts>( - r#" - SELECT * - FROM accounts - ORDER BY uid_number DESC - LIMIT 1 - "#, - ) - .fetch_all(db) - .await - { - if !results.is_empty() { - return results[0].uid_number + 1; - } - } - - 9999 }