From 99b2fbafa0f4ebd5f3c660716c63a46b0983fcdd Mon Sep 17 00:00:00 2001 From: Max Hohlfeld Date: Tue, 16 Jul 2024 14:43:20 +0200 Subject: [PATCH] refactor: create mail state and reword email --- .env | 6 ++++ README.md | 1 + src/endpoints/user/post_reset.rs | 59 +++++++++++++++++++++----------- src/main.rs | 3 ++ src/utils/email.rs | 35 +++++++++++++++++++ src/utils/mod.rs | 1 + 6 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 src/utils/email.rs create mode 100644 src/utils/mod.rs diff --git a/.env b/.env index de02a318..d4772de0 100644 --- a/.env +++ b/.env @@ -4,4 +4,10 @@ DATABASE_URL=postgresql://max@localhost/brass # 64 byte long SECRET_KEY="changeInProdOrHandAb11111111111111111111111111111111111111111111" +HOSTNAME="localhost" +SMTP_SERVER="localhost" +SMTP_PORT="1025" +# SMTP_LOGIN="" +# SMTP_PASSWORD="" +SMTP_TLSTYPE="none" diff --git a/README.md b/README.md index ad4f9a79..ecdd4d21 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,4 @@ ## Useful stuff - cargo-watch, cargo-add +- mailtutan diff --git a/src/endpoints/user/post_reset.rs b/src/endpoints/user/post_reset.rs index 612c9f4f..9124b0bd 100644 --- a/src/endpoints/user/post_reset.rs +++ b/src/endpoints/user/post_reset.rs @@ -1,5 +1,10 @@ +use std::env; + use actix_web::{web, HttpResponse, Responder}; -use lettre::{message::header::ContentType, Message, SmtpTransport, Transport}; +use lettre::{ + message::{header::ContentType, MultiPart, SinglePart}, + Message, SmtpTransport, Transport, +}; use serde::Deserialize; use sqlx::PgPool; @@ -17,7 +22,11 @@ struct ResetPasswordForm { } #[actix_web::post("/reset-password")] -async fn post(form: web::Form, pool: web::Data) -> impl Responder { +async fn post( + form: web::Form, + pool: web::Data, + mailer: web::Data, +) -> impl Responder { if form.email.is_some() && form.token.is_none() && form.password.is_none() @@ -28,35 +37,45 @@ async fn post(form: web::Form, pool: web::Data) -> im .await .unwrap(); - // send email to user + let hostname = env::var("HOSTNAME").unwrap(); + let reset_url = format!("https://{}/reset-password?token={}", hostname, reset.token); + let message = Message::builder() .from("noreply ".parse().unwrap()) .reply_to("noreply ".parse().unwrap()) .to(format!("{} <{}>", user.name, user.email).parse().unwrap()) .subject("Brass: Zurücksetzen des Passworts angefordert") - .header(ContentType::TEXT_PLAIN) - .body(format!(r##" - Hallo {}, + .multipart( + MultiPart::alternative() + .singlepart( + SinglePart::builder() + .header(ContentType::TEXT_PLAIN) + .body(format!(r##"Hallo {}, - hier der Link zur Passwortzurücksetzung: https://brasiwa-leipzig.de/reset-password?token={} - Debug: http://localhost:8080/reset-password?token={} +du hast angefordert, dein Passwort zurückzusetzen. Kopiere dafür folgenden Link in deinen Browser: - Nur 24 Stunden gültig! - Wenn du das nicht angefordert hast, dann hast du nichts weiter zu tun. Vielleicht solltest du deine Email bei haveibeenpawn checken! +{} - Bitte nicht auf die E-Mail antworten. +Bitte beachte, dass der Link nur 24 Stunden gültig ist. Solltest du nichts angefordert haben, dann musst du nichts weiter tun. - Viele Grüße - "##, user.name, reset.token, reset.token)) +Viele Grüße"##, user.name, reset_url)) + ) + .singlepart( + SinglePart::builder() + .header(ContentType::TEXT_HTML) + .body(format!(r##"

Hallo {},

+ +

du hast angefordert, dein Passwort zurückzusetzen. Klicke dafür hier oder kopiere folgenden Link in deinen Browser:

+ +

{}

+ +

Bitte beachte, dass der Link nur 24 Stunden gültig ist. Solltest du nichts angefordert haben, dann musst du nichts weiter tun.

+ +

Viele Grüße

"##, user.name, reset_url, reset_url)) + )) .unwrap(); - let mailer = SmtpTransport::from_url("smtp://localhost:1025") - .unwrap() - .build(); - mailer.send(&message).unwrap(); - - println!("{reset:?}"); } return HttpResponse::Ok().body("E-Mail versandt!"); @@ -91,7 +110,7 @@ async fn post(form: web::Form, pool: web::Data) -> im None, None, None, - None + None, ) .await .unwrap(); diff --git a/src/main.rs b/src/main.rs index da71fc33..eb8e3417 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ mod auth; mod endpoints; mod models; mod middleware; +mod utils; mod filters; mod postgres_session_store; @@ -75,6 +76,7 @@ async fn main() -> anyhow::Result<()> { }; let pool = PgPool::connect(&env::var("DATABASE_URL")?).await?; + let mailer = utils::email::get_mailer()?; let secret_key = Key::from(env::var("SECRET_KEY")?.as_bytes()); let store = SqlxPostgresqlSessionStore::from_pool(pool.clone().into()); @@ -121,6 +123,7 @@ async fn main() -> anyhow::Result<()> { App::new() .app_data(web::Data::new(pool.clone())) + .app_data(web::Data::new(mailer.clone())) .configure(endpoints::init) .wrap(middleware::RedirectToLogin) .wrap(middleware::LoadCurrentUser) diff --git a/src/utils/email.rs b/src/utils/email.rs new file mode 100644 index 00000000..60b79ec4 --- /dev/null +++ b/src/utils/email.rs @@ -0,0 +1,35 @@ +use std::env; + +use lettre::{ + transport::smtp::{authentication::Credentials, extension::ClientId}, + SmtpTransport, +}; + +pub fn get_mailer() -> anyhow::Result { + let server = &env::var("SMTP_SERVER")?; + let port = &env::var("SMTP_PORT")?.parse()?; + let login = &env::var("SMTP_LOGIN") + .and_then(|x| Ok(Some(x))) + .unwrap_or(None); + let password = &env::var("SMTP_PASSWORD") + .and_then(|x| Ok(Some(x))) + .unwrap_or(None); + let tls_type = &env::var("SMTP_TLSTYPE")?; + let hostname = &env::var("HOSTNAME")?; + + let mut builder = match tls_type.as_str() { + "starttls" => SmtpTransport::starttls_relay(server)?.port(*port), + "tls" => SmtpTransport::relay(server)?.port(*port), + _ => SmtpTransport::builder_dangerous(server).port(*port), + }; + + if let (Some(login), Some(password)) = (login, password) { + builder = builder.credentials(Credentials::new(login.to_string(), password.to_string())); + } + + let mailer = builder + .hello_name(ClientId::Domain(hostname.to_string())) + .build(); + + Ok(mailer) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 00000000..aa5f45d4 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod email;