fmt: fmt and clippy

This commit is contained in:
silver 2023-06-04 22:06:34 +01:00
parent f7ac2fa951
commit 2e3578bae7
4 changed files with 151 additions and 130 deletions

View file

@ -1,22 +1,22 @@
pub mod methods; pub mod methods;
use dotenv::dotenv; use dotenv::dotenv;
use ldap3::{LdapConn, Scope, SearchEntry};
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use sqlx::{Error, Pool, Sqlite}; use sqlx::{Error, Pool, Sqlite};
use std::env; use std::env;
use std::str::FromStr; use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use ldap3::{LdapConn, Scope, SearchEntry};
use tide::prelude::*; use tide::prelude::*;
#[derive(Debug, Deserialize, Serialize, sqlx::FromRow)] #[derive(Debug, Deserialize, Serialize, sqlx::FromRow)]
pub struct AccountsPending { pub struct AccountsPending {
user: String, user: String,
mail: String, mail: String,
name_first : String, name_first: String,
name_second : String, name_second: String,
auth_code : String, auth_code: String,
// will only last for a few hours // will only last for a few hours
expiry: i64 expiry: i64,
} }
#[derive(Debug, Deserialize, Serialize, sqlx::FromRow)] #[derive(Debug, Deserialize, Serialize, sqlx::FromRow)]
@ -24,7 +24,7 @@ pub struct Accounts {
user: String, user: String,
uid_number: i64, uid_number: i64,
discord: Option<String>, discord: Option<String>,
enabled: bool enabled: bool,
} }
pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> { pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
@ -55,12 +55,16 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
discord text, discord text,
enabled integer not null enabled integer not null
)", )",
).execute(&pool).await?; )
.execute(&pool)
.await?;
sqlx::query("CREATE INDEX IF NOT EXISTS index_uid_number ON accounts (uid_number)").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; update_accounts(&pool, config).await;
Ok(pool) Ok(pool)
} }
@ -118,16 +122,13 @@ pub fn get_config() -> Config {
config config
} }
async fn update_accounts(pool: &Pool<Sqlite>, config: &Config) { async fn update_accounts(pool: &Pool<Sqlite>, config: &Config) {
let mut ldap = LdapConn::new(&config.ldap_host).unwrap(); let mut ldap = LdapConn::new(&config.ldap_host).unwrap();
ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw).unwrap().success().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(x) = ldap.search("ou=users,dc=skynet,dc=ie", Scope::OneLevel, "(objectClass=*)", vec!["uid", "uidNumber", "skDiscord", "skMemberOf"]) {
if let Ok((rs, _res)) = x.success() { if let Ok((rs, _res)) = x.success() {
for entry in rs { for entry in rs {
let tmp = SearchEntry::construct(entry); let tmp = SearchEntry::construct(entry);
@ -137,7 +138,7 @@ async fn update_accounts(pool: &Pool<Sqlite>, config: &Config) {
discord: None, discord: None,
enabled: false, enabled: false,
}; };
// pull out the required info // pull out the required info
if tmp.attrs.contains_key("uid") && !tmp.attrs["uid"].is_empty() { if tmp.attrs.contains_key("uid") && !tmp.attrs["uid"].is_empty() {
tmp_account.user = tmp.attrs["uid"][0].clone(); tmp_account.user = tmp.attrs["uid"][0].clone();
@ -148,27 +149,26 @@ async fn update_accounts(pool: &Pool<Sqlite>, config: &Config) {
if tmp.attrs.contains_key("skDiscord") && !tmp.attrs["skDiscord"].is_empty() { if tmp.attrs.contains_key("skDiscord") && !tmp.attrs["skDiscord"].is_empty() {
tmp_account.user = tmp.attrs["skDiscord"][0].clone(); 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")){ 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; tmp_account.enabled = true;
} }
if tmp_account.user.len() > 0 { if !tmp_account.user.is_empty() {
sqlx::query_as::<_, Accounts>( sqlx::query_as::<_, Accounts>(
" "
INSERT OR REPLACE INTO accounts (user, uid_number, discord, enabled) INSERT OR REPLACE INTO accounts (user, uid_number, discord, enabled)
VALUES (?1, ?2, ?3, ?4) VALUES (?1, ?2, ?3, ?4)
", ",
) )
.bind(&tmp_account.user) .bind(&tmp_account.user)
.bind(&tmp_account.uid_number) .bind(tmp_account.uid_number)
.bind(&tmp_account.discord) .bind(&tmp_account.discord)
.bind(&tmp_account.enabled) .bind(tmp_account.enabled)
.fetch_optional(pool) .fetch_optional(pool)
.await .await
.ok(); .ok();
} }
} }
} }
} }

View file

@ -1,6 +1,6 @@
use skynet_ldap_server::methods::account_new::{post_new_account, post_new_account_confirmation};
use skynet_ldap_server::methods::account_update::post_update_ldap; use skynet_ldap_server::methods::account_update::post_update_ldap;
use skynet_ldap_server::{db_init, get_config, State}; use skynet_ldap_server::{db_init, get_config, State};
use skynet_ldap_server::methods::account_new::{post_new_account, post_new_account_confirmation};
#[async_std::main] #[async_std::main]
async fn main() -> tide::Result<()> { async fn main() -> tide::Result<()> {
@ -19,7 +19,7 @@ async fn main() -> tide::Result<()> {
let mut app = tide::with_state(state); let mut app = tide::with_state(state);
app.at("/ldap/update").post(post_update_ldap); app.at("/ldap/update").post(post_update_ldap);
app.at("/ldap/new").post(post_new_account); app.at("/ldap/new").post(post_new_account);
app.at("/ldap/new/verify").post(post_new_account_confirmation); app.at("/ldap/new/verify").post(post_new_account_confirmation);

View file

@ -1,18 +1,18 @@
use crate::{Accounts, AccountsPending, get_now, State}; use crate::{get_now, Accounts, AccountsPending, State};
use ldap3::exop::PasswordModify; use ldap3::exop::PasswordModify;
use ldap3::{LdapConn, Mod, Scope, SearchEntry}; use ldap3::{LdapConn, Scope};
use sqlx::{Pool, Sqlite};
use std::collections::HashSet; use std::collections::HashSet;
use tide::prelude::{json, Deserialize}; use tide::prelude::{json, Deserialize};
use tide::Request; use tide::Request;
use sqlx::{Pool, Sqlite};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct LdapNewUser { pub struct LdapNewUser {
user: String, user: String,
// email that is used on wolves // email that is used on wolves
mail: String, mail: String,
name_first : String, name_first: String,
name_second : String name_second: String,
} }
/// Handles initial detail entering page /// Handles initial detail entering page
@ -27,41 +27,40 @@ pub async fn post_new_account(mut req: Request<State>) -> tide::Result {
// if there is then send email with link to the account // if there is then send email with link to the account
// save user details in the db // save user details in the db
let LdapNewUser { let LdapNewUser {
user, user,
mail, mail,
name_first, name_first,
name_second name_second,
} = req.body_json().await?; } = req.body_json().await?;
let config = &req.state().config; let config = &req.state().config;
// easier to give each request its own connection // easier to give each request its own connection
let mut ldap = LdapConn::new(&config.ldap_host)?; let mut ldap = LdapConn::new(&config.ldap_host)?;
// ldap3 docs say a blank username and pass is an anon bind // ldap3 docs say a blank username and pass is an anon bind
ldap.simple_bind("", "")?.success()?; ldap.simple_bind("", "")?.success()?;
let filter_dn = format!("(uid={})", &user); let filter_dn = format!("(uid={})", &user);
if let Ok(x) = ldap.search("ou=users,dc=skynet,dc=ie", Scope::OneLevel, &filter_dn, vec!["*"]) { if let Ok(x) = ldap.search("ou=users,dc=skynet,dc=ie", Scope::OneLevel, &filter_dn, vec!["*"]) {
if let Ok((rs, _res)) = x.success(){ if let Ok((rs, _res)) = x.success() {
if !rs.is_empty() { if !rs.is_empty() {
return Ok(json!({"result": "error", "error": "username not available"}).into()) return Ok(json!({"result": "error", "error": "username not available"}).into());
} }
} }
} }
let filter_email = format!("(mail={})", mail); let filter_email = format!("(mail={})", mail);
if let Ok(x) = ldap.search("ou=users,dc=skynet,dc=ie", Scope::OneLevel, &filter_email, vec!["*"]) { if let Ok(x) = ldap.search("ou=users,dc=skynet,dc=ie", Scope::OneLevel, &filter_email, vec!["*"]) {
if let Ok((rs, _res)) = x.success(){ if let Ok((rs, _res)) = x.success() {
if !rs.is_empty() { if !rs.is_empty() {
return Ok(json!({"result": "error", "error": "email in use"}).into()) return Ok(json!({"result": "error", "error": "email in use"}).into());
} }
} }
} }
// done with ldap // done with ldap
ldap.unbind()?; ldap.unbind()?;
@ -69,7 +68,7 @@ pub async fn post_new_account(mut req: Request<State>) -> tide::Result {
let pool = &req.state().db; let pool = &req.state().db;
db_pending_clear_expired(pool).await; db_pending_clear_expired(pool).await;
// now check local // now check local
if let Ok(results) = sqlx::query_as::<_, AccountsPending>( if let Ok(results) = sqlx::query_as::<_, AccountsPending>(
r#" r#"
@ -77,9 +76,13 @@ pub async fn post_new_account(mut req: Request<State>) -> tide::Result {
FROM accounts_pending FROM accounts_pending
WHERE user == ? WHERE user == ?
"#, "#,
).bind(&user).fetch_all(pool).await { )
if !results.is_empty(){ .bind(&user)
return Ok(json!({"result": "error", "error": "username not available"}).into()) .fetch_all(pool)
.await
{
if !results.is_empty() {
return Ok(json!({"result": "error", "error": "username not available"}).into());
} }
} }
@ -89,24 +92,28 @@ pub async fn post_new_account(mut req: Request<State>) -> tide::Result {
FROM accounts_pending FROM accounts_pending
WHERE mail == ? WHERE mail == ?
"#, "#,
).bind(&mail).fetch_all(pool).await { )
if !results.is_empty(){ .bind(&mail)
return Ok(json!({"result": "error", "error": "email in use"}).into()) .fetch_all(pool)
.await
{
if !results.is_empty() {
return Ok(json!({"result": "error", "error": "email in use"}).into());
} }
} }
// frontend now tells user to check their email // frontend now tells user to check their email
/* /*
TODO: TODO:
now check with wolves to see if the email is already activated now check with wolves to see if the email is already activated
use email as primary match use email as primary match
then search up to see if teh wolves ID has a match then search up to see if teh wolves ID has a match
if not generate tuhe user and send email if not generate tuhe user and send email
*/ */
let auth_code = create_random_string(50); let auth_code = create_random_string(50);
// 1 hour expiry // 1 hour expiry
let expiry = get_now() + (60 * 60); let expiry = get_now() + (60 * 60);
@ -116,24 +123,23 @@ pub async fn post_new_account(mut req: Request<State>) -> tide::Result {
VALUES (?1, ?2, ?3, ?4, ?5, ?6) VALUES (?1, ?2, ?3, ?4, ?5, ?6)
"#, "#,
) )
.bind(&user) .bind(&user)
.bind(&mail) .bind(&mail)
.bind(&name_first) .bind(&name_first)
.bind(&name_second) .bind(&name_second)
.bind(&auth_code) .bind(&auth_code)
.bind(&expiry) .bind(expiry)
.fetch_optional(pool) .fetch_optional(pool)
.await .await
.ok(); .ok();
// TODO: Send email with auth_code // TODO: Send email with auth_code
Ok(json!({"result": "success"}).into()) Ok(json!({"result": "success"}).into())
} }
// clear the db of expired ones before checking for username and validating inputs // clear the db of expired ones before checking for username and validating inputs
async fn db_pending_clear_expired(pool: &Pool<Sqlite>){ async fn db_pending_clear_expired(pool: &Pool<Sqlite>) {
let now = get_now(); let now = get_now();
if let Ok(results) = sqlx::query_as::<_, AccountsPending>( if let Ok(results) = sqlx::query_as::<_, AccountsPending>(
r#" r#"
@ -141,37 +147,37 @@ async fn db_pending_clear_expired(pool: &Pool<Sqlite>){
FROM accounts_pending FROM accounts_pending
WHERE expiry < ? WHERE expiry < ?
"#, "#,
).bind(now).fetch_all(pool).await { )
.bind(now)
.fetch_all(pool)
.await
{
println!("{:?}", results) println!("{:?}", results)
} }
} }
fn create_random_string(length: usize) -> String { fn create_random_string(length: usize) -> String {
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric; use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
thread_rng() thread_rng().sample_iter(&Alphanumeric).take(length).map(char::from).collect()
.sample_iter(&Alphanumeric)
.take(length)
.map(char::from)
.collect()
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct LdapNewUserVerify { pub struct LdapNewUserVerify {
auth_code: String, auth_code: String,
password: String password: String,
} }
pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Result { pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Result {
let LdapNewUserVerify { let LdapNewUserVerify {
auth_code, auth_code,
password password,
} = req.body_json().await?; } = req.body_json().await?;
let State { let State {
db, db,
config, config,
.. ..
} = &req.state(); } = &req.state();
// make sure to clear out the expired ones first // make sure to clear out the expired ones first
@ -179,12 +185,16 @@ pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Res
// search db for auth_code // search db for auth_code
let results = sqlx::query_as::<_, AccountsPending>( let results = sqlx::query_as::<_, AccountsPending>(
r#" r#"
SELECT * SELECT *
FROM accounts_pending FROM accounts_pending
WHERE auth_code == ? WHERE auth_code == ?
"#, "#,
).bind(auth_code).fetch_all(db).await.unwrap_or(vec![]); )
.bind(&auth_code)
.fetch_all(db)
.await
.unwrap_or(vec![]);
if results.is_empty() { if results.is_empty() {
return Ok(json!({"result": "error"}).into()); return Ok(json!({"result": "error"}).into());
@ -194,8 +204,14 @@ pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Res
// need to bind as admin // need to bind as admin
ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?; ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?;
let AccountsPending{ user, mail, name_first, name_second, auth_code, expiry } = &results[0]; let AccountsPending {
user,
mail,
name_first,
name_second,
..
} = &results[0];
let dn = format!("uid={},ou=users,dc=skynet,dc=ie", user); let dn = format!("uid={},ou=users,dc=skynet,dc=ie", user);
let uid_number = get_max_uid_number(db).await; let uid_number = get_max_uid_number(db).await;
let home_directory = format!("/home/{}", user); let home_directory = format!("/home/{}", user);
@ -204,35 +220,34 @@ pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Res
let labeled_uri = format!("ldap:///ou=groups,dc=skynet,dc=ie??sub?(&(objectclass=posixgroup)(memberuid={}))", user); 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_mail = format!("{}@skynet.ie", user);
let sk_created = get_sk_created(); let sk_created = get_sk_created();
// create user // create user
ldap.add(&dn, vec![ ldap.add(
("objectClass", HashSet::from(["top", "person", "posixaccount", "ldapPublicKey", "inetOrgPerson", "skPerson"])), &dn,
vec![
// top ("objectClass", HashSet::from(["top", "person", "posixaccount", "ldapPublicKey", "inetOrgPerson", "skPerson"])),
("ou", HashSet::from(["users"])), // top
("ou", HashSet::from(["users"])),
// person // person
("uid", HashSet::from([user.as_str()])), ("uid", HashSet::from([user.as_str()])),
("cn", HashSet::from([cn.as_str()])), ("cn", HashSet::from([cn.as_str()])),
// posixaccount
// posixaccount ("uidNumber", HashSet::from([uid_number.to_string().as_str()])),
("uidNumber", HashSet::from([uid_number.to_string().as_str()])), ("gidNumber", HashSet::from(["1001"])),
("gidNumber", HashSet::from(["1001"])), ("homedirectory", HashSet::from([home_directory.as_str()])),
("homedirectory", HashSet::from([home_directory.as_str()])), ("userpassword", HashSet::from([password_tmp.as_str()])),
("userpassword", HashSet::from([password_tmp.as_str()])), // inetOrgPerson
("mail", HashSet::from([mail.as_str()])),
// inetOrgPerson ("sn", HashSet::from([name_second.as_str()])),
("mail", HashSet::from([mail.as_str()])), // skPerson
("sn", HashSet::from([name_second.as_str()])), ("labeledURI", HashSet::from([labeled_uri.as_str()])),
("skMail", HashSet::from([sk_mail.as_str()])),
// skPerson
("labeledURI", HashSet::from([labeled_uri.as_str()])),
("skMail", HashSet::from([sk_mail.as_str()])),
// need to get this from wolves // need to get this from wolves
//("skID", HashSet::from(["12345678"])), //("skID", HashSet::from(["12345678"])),
("skCreated", HashSet::from([sk_created.as_str()])), ("skCreated", HashSet::from([sk_created.as_str()])),
])?.success()?; ],
)?
.success()?;
// now to properly set teh password // now to properly set teh password
let tmp = PasswordModify { let tmp = PasswordModify {
@ -244,14 +259,18 @@ pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Res
// done with ldap // done with ldap
ldap.unbind()?; ldap.unbind()?;
// delete from tmp db // delete from tmp db
if let Ok(results) = sqlx::query_as::<_, AccountsPending>( if let Ok(results) = sqlx::query_as::<_, AccountsPending>(
r#" r#"
DELETE FROM accounts_pending DELETE FROM accounts_pending
WHERE auth_code == ? WHERE auth_code == ?
"#, "#,
).bind(&auth_code).fetch_all(db).await { )
.bind(&auth_code)
.fetch_all(db)
.await
{
println!("{:?}", results) println!("{:?}", results)
} }
@ -262,20 +281,19 @@ pub async fn post_new_account_confirmation(mut req: Request<State>) -> tide::Res
VALUES (?1, ?2, ?3) VALUES (?1, ?2, ?3)
", ",
) )
.bind(&user) .bind(user)
.bind(&uid_number) .bind(uid_number)
.bind(false) .bind(false)
.fetch_optional(db) .fetch_optional(db)
.await .await
.ok(); .ok();
// frontend tells user that initial password ahs been sent to tehm // frontend tells user that initial password ahs been sent to tehm
Ok(json!({"result": "success"}).into()) Ok(json!({"result": "success"}).into())
} }
fn get_sk_created() -> String { fn get_sk_created() -> String {
use chrono::{Utc}; use chrono::Utc;
let now = Utc::now(); let now = Utc::now();
format!("{}", now.format("%Y%m%d%H%M%SZ")) format!("{}", now.format("%Y%m%d%H%M%SZ"))
@ -289,11 +307,14 @@ pub async fn get_max_uid_number(db: &Pool<Sqlite>) -> i64 {
ORDER BY uid_number DESC ORDER BY uid_number DESC
LIMIT 1 LIMIT 1
"#, "#,
).fetch_all(db).await { )
.fetch_all(db)
.await
{
if !results.is_empty() { if !results.is_empty() {
return results[0].uid_number + 1; return results[0].uid_number + 1;
} }
} }
9999 9999
} }

View file

@ -1,2 +1,2 @@
pub mod account_update;
pub mod account_new; pub mod account_new;
pub mod account_update;