only confirmed users can login

Also uses a forked version of `maik` that supports getting mail bodies.
This commit is contained in:
azdle 2025-07-25 12:19:59 -05:00
parent c7500ae6a3
commit ef3cc5a11b
5 changed files with 35 additions and 14 deletions

2
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View file

@ -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")
}

View file

@ -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")

View file

@ -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
}