Add an admin flag to user
authorGreg Burri <greg.burri@gmail.com>
Mon, 20 Jan 2025 00:25:19 +0000 (01:25 +0100)
committerGreg Burri <greg.burri@gmail.com>
Mon, 20 Jan 2025 00:25:19 +0000 (01:25 +0100)
backend/src/data/db/recipe.rs
backend/src/data/db/user.rs
backend/src/data/model.rs
backend/src/services/recipe.rs
backend/templates/recipe_view.html

index 64ed972..e59b064 100644 (file)
@@ -61,7 +61,12 @@ ORDER BY [title]
 
     pub async fn can_edit_recipe(&self, user_id: i64, recipe_id: i64) -> Result<bool> {
         sqlx::query_scalar(
-            r#"SELECT COUNT(*) = 1 FROM [Recipe] WHERE [id] = $1 AND [user_id] = $2"#,
+            r#"
+SELECT COUNT(*) = 1
+FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
+WHERE [Recipe].[id] = $1 AND ([is_admin] OR [user_id] = $2)
+            "#,
         )
         .bind(recipe_id)
         .bind(user_id)
@@ -75,8 +80,9 @@ ORDER BY [title]
             r#"
 SELECT COUNT(*) = 1
 FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
 INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
-WHERE [Group].[id] = $1 AND [user_id] = $2
+WHERE [Group].[id] = $1 AND ([is_admin] OR [user_id] = $2)
             "#,
         )
         .bind(group_id)
@@ -98,8 +104,9 @@ WHERE [Group].[id] = $1 AND [user_id] = $2
             r#"
 SELECT COUNT(*)
 FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
 INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
-WHERE [Group].[id] IN ({}) AND [user_id] = $1
+WHERE [Group].[id] IN ({}) AND ([is_admin] OR [user_id] = $2)
             "#,
             params
         );
@@ -116,9 +123,10 @@ WHERE [Group].[id] IN ({}) AND [user_id] = $1
             r#"
 SELECT COUNT(*) = 1
 FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
 INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
 INNER JOIN [Step] ON [Step].[group_id] = [Group].[id]
-WHERE [Step].[id] = $1 AND [user_id] = $2
+WHERE [Step].[id] = $1 AND ([is_admin] OR [user_id] = $2)
             "#,
         )
         .bind(step_id)
@@ -136,9 +144,10 @@ WHERE [Step].[id] = $1 AND [user_id] = $2
             r#"
 SELECT COUNT(*)
 FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
 INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
 INNER JOIN [Step] ON [Step].[group_id] = [Group].[id]
-WHERE [Step].[id] IN ({}) AND [user_id] = $1
+WHERE [Step].[id] IN ({}) AND ([is_admin] OR [user_id] = $2)
             "#,
             params
         );
@@ -159,10 +168,11 @@ WHERE [Step].[id] IN ({}) AND [user_id] = $1
             r#"
 SELECT COUNT(*)
 FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
 INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
 INNER JOIN [Step] ON [Step].[group_id] = [Group].[id]
 INNER JOIN [Ingredient] ON [Ingredient].[step_id] = [Step].[id]
-WHERE [Ingredient].[id] = $1 AND [user_id] = $2
+WHERE [Ingredient].[id] = $1 AND ([is_admin] OR [user_id] = $2)
             "#,
         )
         .bind(ingredient_id)
@@ -184,10 +194,11 @@ WHERE [Ingredient].[id] = $1 AND [user_id] = $2
             r#"
 SELECT COUNT(*)
 FROM [Recipe]
+INNER JOIN [User] ON [User].id = [Recipe].user_id
 INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
 INNER JOIN [Step] ON [Step].[group_id] = [Group].[id]
 INNER JOIN [Ingredient] ON [Ingredient].[step_id] = [Step].[id]
-WHERE [Ingredient].[id] IN ({}) AND [user_id] = $1
+WHERE [Ingredient].[id] IN ({}) AND ([is_admin] OR [user_id] = $2)
             "#,
             params
         );
index e23fd38..4b7d2b4 100644 (file)
@@ -76,11 +76,13 @@ FROM [UserLoginToken] WHERE [token] = $1
     }
 
     pub async fn load_user(&self, user_id: i64) -> Result<Option<model::User>> {
-        sqlx::query_as("SELECT [id], [email], [name], [lang] FROM [User] WHERE [id] = $1")
-            .bind(user_id)
-            .fetch_optional(&self.pool)
-            .await
-            .map_err(DBError::from)
+        sqlx::query_as(
+            "SELECT [id], [email], [name], [lang], [is_admin] FROM [User] WHERE [id] = $1",
+        )
+        .bind(user_id)
+        .fetch_optional(&self.pool)
+        .await
+        .map_err(DBError::from)
     }
 
     /// If a new email is given and it doesn't match the current one then it has to be
index a0aff3b..ceb7128 100644 (file)
@@ -8,6 +8,7 @@ pub struct User {
     pub name: String,
     pub email: String,
     pub lang: String,
+    pub is_admin: bool,
 }
 
 #[derive(Debug, FromRow)]
@@ -39,6 +40,10 @@ pub struct Recipe {
     pub groups: Vec<Group>,
 }
 
+pub fn can_user_edit_recipe(user: &User, recipe: &Recipe) -> bool {
+    user.is_admin || recipe.user_id == user.id
+}
+
 #[derive(Debug, FromRow)]
 pub struct Group {
     pub id: i64,
index 160b6d1..09eb138 100644 (file)
@@ -36,7 +36,7 @@ pub async fn edit_recipe(
 ) -> Result<Response> {
     if let Some(user) = user {
         if let Some(recipe) = connection.get_recipe(recipe_id, false).await? {
-            if recipe.user_id == user.id {
+            if model::can_user_edit_recipe(&user, &recipe) {
                 let recipes = Recipes {
                     published: connection
                         .get_all_published_recipe_titles(tr.current_lang_code(), Some(user.id))
index 39aea85..989735e 100644 (file)
@@ -5,7 +5,7 @@
 <div class="content" id="recipe-view">
     <h2 class="recipe-title" >{{ recipe.title }}</h2>
 
-    {% if user.is_some() && recipe.user_id == user.as_ref().unwrap().id %}
+    {% if user.is_some() && crate::data::model::can_user_edit_recipe(&user.as_ref().unwrap(), &recipe) %}
         <a class="edit-recipe" href="/recipe/edit/{{ recipe.id }}" >Edit</a>
     {% endif %}