From ef3cc5a11b6657748831fff026ce142d9514e1ea Mon Sep 17 00:00:00 2001 From: azdle Date: Fri, 25 Jul 2025 12:19:59 -0500 Subject: [PATCH] only confirmed users can login Also uses a forked version of `maik` that supports getting mail bodies. --- Cargo.lock | 2 -- Cargo.toml | 3 +++ src/server/routes/auth/mod.rs | 7 +++---- tests/auth.rs | 19 +++++++++++++++++++ tests/subscriptions.rs | 18 ++++++++++-------- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d13960a..7c38af1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1651,8 +1651,6 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "maik" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba8f0b47dc7cc74760332828e060ec705339b5e863894f575d81691546bbc836" dependencies = [ "base64 0.22.1", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 70a49e4..15b3155 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,3 +50,6 @@ test-log = { version = "0.2.12", default-features = false, features = ["trace"] [profile.dev.package.sqlx-macros] opt-level = 3 + +[patch.crates-io] +maik = { path = "../downloads/maik" } diff --git a/src/server/routes/auth/mod.rs b/src/server/routes/auth/mod.rs index 1aa53dc..3081e8b 100644 --- a/src/server/routes/auth/mod.rs +++ b/src/server/routes/auth/mod.rs @@ -249,10 +249,9 @@ pub async fn login( return Err(LoginError::CsrfValidationFailed); } - // TODO: `status = 'confirmed' AND `, need to be able to get email contents in test let user = sqlx::query!( r#" - SELECT id, password FROM users WHERE email = $1 LIMIT 1; + SELECT id, password FROM users WHERE status = 'confirmed' AND email = $1 LIMIT 1; "#, form.email ) @@ -461,7 +460,7 @@ pub async fn signup_confirm( #[derive(thiserror::Error, Debug)] pub enum SignupConfirmError { - #[error("Not Logged In")] + #[error("Invalid Token")] InvalidToken, #[error("CSRF Validation Failed")] CsrfValidationFailed, @@ -472,7 +471,7 @@ pub enum SignupConfirmError { impl IntoResponse for SignupConfirmError { fn into_response(self) -> axum::response::Response { let (status, message) = match self { - SignupConfirmError::InvalidToken => (StatusCode::UNAUTHORIZED, "Unknown User"), + SignupConfirmError::InvalidToken => (StatusCode::UNAUTHORIZED, "Invalid Token"), SignupConfirmError::CsrfValidationFailed => { (StatusCode::BAD_REQUEST, "CSRF Validation Failed, Try Again") } diff --git a/tests/auth.rs b/tests/auth.rs index da87577..4d6613c 100644 --- a/tests/auth.rs +++ b/tests/auth.rs @@ -2,6 +2,7 @@ pub mod fixture; use fixture::TestServer; use anyhow::Result; +use regex::Regex; use test_log::test as traced; #[traced(tokio::test)] @@ -19,6 +20,24 @@ async fn login_succeeds_with_valid_credentials() -> Result<()> { assert_eq!(resp.status(), 200, "signup succeeds"); + let mail = server.mock_smtp_server.receive_mail().expect("recv mail"); + let link_regex = Regex::new(r"/auth/confirm\?token\=([a-zA-Z0-9]+)")?; + let confirm_link = link_regex + .find(&mail.body) + .expect("find link in body") + .as_str(); + + println!("link: {confirm_link}"); + + // Confirm Account + let resp = client + .post(confirm_link) + .csrf_form::<[(&str, &str); 0]>(&[]) + .send() + .await?; + + assert_eq!(resp.status(), 200, "confirm succeeds"); + // Login let resp = client .post("/auth/login") diff --git a/tests/subscriptions.rs b/tests/subscriptions.rs index ba3422b..a5b39c8 100644 --- a/tests/subscriptions.rs +++ b/tests/subscriptions.rs @@ -2,8 +2,7 @@ pub mod fixture; use fixture::TestServer; use anyhow::Result; -use maik::MailAssertion; -use regex::bytes::Regex; +use regex::Regex; use test_log::test as traced; #[traced(tokio::test)] @@ -21,12 +20,15 @@ async fn subscribe_succeeds_with_valid_input() -> Result<()> { assert_eq!(resp.status(), 200, "subscribe succeeds"); - assert!( - server - .mock_smtp_server - .assert(MailAssertion::new().body_matches(Regex::new(r"http\:\/\/localhost\/")?)), - "email sent has link" - ); + let mail = server.mock_smtp_server.receive_mail().expect("recv mail"); + let link_regex = + Regex::new(r"http\:\/\/localhost\/subscriptions/confirm/\?token\=[a-zA-Z0-9]+")?; + let link = link_regex + .find(&mail.body) + .expect("find link in body") + .as_str(); + + println!("link: {link}"); server.shutdown().await }