From c8e0aa918cb37a54fffb4e374819beeed9037a34 Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Wed, 30 Apr 2025 11:11:01 +0200 Subject: [PATCH] Check user is validated before creating a new recipe --- Cargo.lock | 1 + backend/Cargo.toml | 2 ++ backend/src/data/db/mod.rs | 3 +++ backend/src/data/db/recipe.rs | 50 +++++++++++++++++++++++++++++++++++ backend/src/data/db/user.rs | 9 ++++--- 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 11ef38a..15d84cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2460,6 +2460,7 @@ dependencies = [ "clap", "common", "comrak", + "cookie", "itertools", "lettre", "rand 0.9.1", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 615aff4..ae59e93 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -50,6 +50,8 @@ lettre = { version = "0.11", default-features = false, features = [ thiserror = "2" +# Integration tests dependencies. [dev-dependencies] axum-test = "17" +cookie = "0.18" scraper = "0.23" diff --git a/backend/src/data/db/mod.rs b/backend/src/data/db/mod.rs index d6f02b0..0dc0cdd 100644 --- a/backend/src/data/db/mod.rs +++ b/backend/src/data/db/mod.rs @@ -33,6 +33,9 @@ pub enum DBError { )] UnsupportedVersion(u32), + #[error("User not validated")] + UserNotValidated, + #[error("Unknown error: {0}")] Other(String), } diff --git a/backend/src/data/db/recipe.rs b/backend/src/data/db/recipe.rs index 3e370ae..44ca62e 100644 --- a/backend/src/data/db/recipe.rs +++ b/backend/src/data/db/recipe.rs @@ -281,6 +281,21 @@ FROM [Recipe] WHERE [id] = $1 pub async fn create_recipe(&self, user_id: i64) -> Result { let mut tx = self.tx().await?; + // Check that the user is validated. + if !sqlx::query_scalar( + r#" +SELECT COUNT(*) = 1 +FROM [User] WHERE +[User].[id] = $1 AND [User].[validation_token] IS NULL + "#, + ) + .bind(user_id) + .fetch_one(&mut *tx) + .await? + { + return Err(DBError::UserNotValidated); + } + // Search for an existing empty recipe and return its id instead of creating a new one. match sqlx::query_scalar::<_, i64>( r#" @@ -944,6 +959,19 @@ mod tests { Ok(()) } + #[tokio::test] + async fn create_a_new_recipe_by_an_unvalidated_user() -> Result<()> { + let connection = Connection::new_in_memory().await?; + + let user_id = create_an_unvalidated_user(&connection).await?; + match connection.create_recipe(user_id).await { + Err(DBError::UserNotValidated) => (), // Nominal case. + other => panic!("{:?}", other), + } + + Ok(()) + } + #[tokio::test] async fn setters() -> Result<()> { let connection = Connection::new_in_memory().await?; @@ -977,6 +1005,28 @@ mod tests { Ok(()) } + async fn create_an_unvalidated_user(connection: &Connection) -> Result { + let user_id = 1; + connection.execute_sql( + sqlx::query( + r#" +INSERT INTO [User] + ([id], [email], [name], [creation_datetime], [password], [validation_token_datetime], [validation_token]) +VALUES + ($1, $2, $3, $4, $5, $6, $7) + "# + ) + .bind(user_id) + .bind("paul@atreides.com") + .bind("paul") + .bind("") + .bind("$argon2id$v=19$m=4096,t=3,p=1$G4fjepS05MkRbTqEImUdYg$GGziE8uVQe1L1oFHk37lBno10g4VISnVqynSkLCH3Lc") + .bind("2022-11-29 22:05:04.121407300+00:00") + .bind(Some("SOME_TOKEN")) // not 'null'. + ).await?; + Ok(user_id) + } + async fn create_a_user(connection: &Connection) -> Result { let user_id = 1; connection.execute_sql( diff --git a/backend/src/data/db/user.rs b/backend/src/data/db/user.rs index fcca817..6948143 100644 --- a/backend/src/data/db/user.rs +++ b/backend/src/data/db/user.rs @@ -27,7 +27,8 @@ pub enum UpdateUserResult { pub enum ValidationResult { UnknownUser, ValidationExpired, - Ok(String, i64), // Returns token and user id. + /// Returns token and user id. + Ok(String, i64), } #[derive(Debug, Display)] @@ -35,13 +36,15 @@ pub enum SignInResult { UserNotFound, WrongPassword, AccountNotValidated, - Ok(String, i64), // Returns token and user id. + /// Returns token and user id. + Ok(String, i64), } #[derive(Debug, Display)] pub enum AuthenticationResult { NotValidToken, - Ok(i64), // Returns user id. + /// Returns user id. + Ok(i64), } #[derive(Debug, Display)]