feat: now sends a reset pw mail to all connected emails
All checks were successful
Build / build (push) Successful in 7m49s
Build / deploy (push) Successful in 25s

This commit is contained in:
silver 2025-03-11 01:10:38 +00:00
parent 6510696851
commit b00b5cac6d
Signed by: silver
GPG key ID: 36F93D61BAD3FD7D
3 changed files with 90 additions and 33 deletions

View file

@ -9,6 +9,7 @@ async fn main() -> tide::Result<()> {
let db = db_init(&config).await.unwrap();
update_wolves(&config, &db).await;
update_wolves_id(&db).await;
update_ldap(&config, &db).await;
Ok(())
@ -21,6 +22,30 @@ async fn update_wolves(config: &Config, db: &Pool<Sqlite>) {
}
}
async fn update_wolves_id(db: &Pool<Sqlite>) {
match sqlx::query(
"UPDATE accounts
SET id_wolves=(
SELECT id_wolves
FROM accounts_wolves
WHERE accounts.mail=accounts_wolves.email
)
WHERE (
SELECT id_wolves
FROM accounts_wolves
WHERE accounts.mail=accounts_wolves.email
) IS NOT NULL",
)
.execute(db)
.await
{
Ok(_) => {}
Err(e) => {
dbg!(e);
}
}
}
async fn update_ldap(config: &Config, db: &Pool<Sqlite>) {
let mut ldap = match LdapConn::new(&config.ldap_host) {
Ok(s) => s,

View file

@ -63,13 +63,13 @@ pub struct Accounts {
pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
let database = format!("{}/{}", &config.home, &config.database);
let pool = SqlitePoolOptions::new()
.max_connections(5)
.connect_with(
SqliteConnectOptions::from_str(&format!("sqlite://{}", database))?
.foreign_keys(true)
.create_if_missing(true),
)
.await?;
.max_connections(5)
.connect_with(
SqliteConnectOptions::from_str(&format!("sqlite://{}", database))?
.foreign_keys(true)
.create_if_missing(true),
)
.await?;
// migrations are amazing!
sqlx::migrate!("./db").run(&pool).await?;

View file

@ -1,4 +1,4 @@
use crate::{get_now_iso, random_string, uid_to_dn, Accounts, AccountsReset, Config, State};
use crate::{get_now_iso, random_string, uid_to_dn, AccountsReset, Config, State};
use chrono::{Duration, SecondsFormat, Utc};
use ldap3::{exop::PasswordModify, LdapConn};
use lettre::{
@ -15,6 +15,7 @@ use tide::{
pub mod password {
use super::*;
use serde::Serialize;
#[derive(Debug, Deserialize)]
struct PassReset {
@ -49,19 +50,27 @@ pub mod password {
Some(x) => x,
};
let mail_is_skynet = user_details.mail.trim().ends_with("@skynet.ie");
let mut mails = vec![];
// user does not have a different email address set
if mail_is_skynet && &user_details.student_id == "00000000" {
// not returning an error here as there is no need to let the person requesting what email the user has
return Ok(json!({"result": "success"}).into());
// dummy mail to send to the studentmail.
if let Some(x) = check_email("dummy@skynet.ie", &user_details.student_id) {
if !mails.contains(&x) {
mails.push(x);
}
}
if let Some(x) = check_email(&user_details.mail, &user_details.student_id) {
if !mails.contains(&x) {
mails.push(x);
}
}
if let Some(x) = check_email(&user_details.email, &user_details.student_id) {
if !mails.contains(&x) {
mails.push(x);
}
}
let mail = if mail_is_skynet {
format!("{}@studentmail.ul.ie", &user_details.student_id)
} else {
user_details.mail
};
// auth is shared for all emails
let auth = random_string(50);
// check if a recent password reset request happened lately
db_pending_clear_expired(db).await?;
@ -71,21 +80,37 @@ pub mod password {
return Ok(json!({"result": "success"}).into());
}
// send mail
let auth = random_string(50);
match send_mail(config, &user_details.user, &mail, &auth) {
Ok(_) => {
save_to_db(db, &user_details.user, &auth).await?;
}
Err(e) => {
println!("{:?}", e);
for mail in mails {
// send mail
match send_mail(config, &user_details.user, &mail, &auth) {
Ok(_) => {
save_to_db(db, &user_details.user, &auth).await?;
}
Err(e) => {
println!("{:?}", e);
}
}
}
Ok(json!({"result": "success"}).into())
}
fn check_email(mail: &str, student_id: &str) -> Option<String> {
let mail_is_skynet = mail.trim().ends_with("@skynet.ie");
// user does not have a different email address set
if mail_is_skynet && student_id == "00000000" {
// not returning an error here as there is no need to let the person requesting what email the user has
return None;
}
if mail_is_skynet {
Some(format!("{}@studentmail.ul.ie", student_id))
} else {
Some(mail.to_string())
}
}
#[derive(Debug, Deserialize)]
pub struct PassResetAuth {
auth: String,
@ -120,7 +145,14 @@ pub mod password {
Ok(json!({"result": "success", "success": "Password set"}).into())
}
pub async fn db_get_user(pool: &Pool<Sqlite>, user_in: &Option<String>, mail_in: &Option<String>) -> Option<Accounts> {
#[derive(Debug, Clone, Deserialize, Serialize, sqlx::FromRow)]
pub struct AccountsRecovery {
pub user: String,
pub mail: String,
pub student_id: String,
pub email: String,
}
pub async fn db_get_user(pool: &Pool<Sqlite>, user_in: &Option<String>, mail_in: &Option<String>) -> Option<AccountsRecovery> {
let user = match user_in {
None => "",
Some(x) => x,
@ -130,11 +162,11 @@ pub mod password {
Some(x) => x,
};
if let Ok(res) = sqlx::query_as::<_, Accounts>(
if let Ok(res) = sqlx::query_as::<_, AccountsRecovery>(
r#"
SELECT *
FROM accounts
WHERE user == ? OR mail ==?
FROM accounts JOIN accounts_wolves ON accounts.id_wolves = accounts_wolves.id_wolves
WHERE accounts.user == ?1 OR accounts.mail == ?2 OR accounts_wolves.email == ?2
"#,
)
.bind(user)
@ -316,7 +348,7 @@ pub mod password {
}
pub mod username {
use super::password::db_get_user;
use super::password::{db_get_user, AccountsRecovery};
use super::*;
// far simpler, accept email, send notification via email
@ -360,7 +392,7 @@ pub mod username {
Ok(json!({"result": "success"}).into())
}
fn send_mail(config: &Config, record: &Accounts) -> Result<Response, Error> {
fn send_mail(config: &Config, record: &AccountsRecovery) -> Result<Response, Error> {
let recipient = &record.user;
let mail = &record.mail;
let discord = "https://discord.skynet.ie";