user-role-cluster (#761)
Reviewed-on: #761
This commit was merged in pull request #761.
	This commit is contained in:
		| @@ -1,5 +1,7 @@ | ||||
| use std::ops::DerefMut; | ||||
|  | ||||
| use serde::Serialize; | ||||
| use sqlx::{sqlite::SqliteQueryResult, FromRow, SqlitePool}; | ||||
| use sqlx::{sqlite::SqliteQueryResult, FromRow, Sqlite, SqlitePool, Transaction}; | ||||
|  | ||||
| use super::user::User; | ||||
|  | ||||
| @@ -22,6 +24,15 @@ impl Family { | ||||
|             .unwrap() | ||||
|     } | ||||
|  | ||||
|     pub async fn insert_tx(db: &mut Transaction<'_, Sqlite>) -> i64 { | ||||
|         let result: SqliteQueryResult = sqlx::query("INSERT INTO family DEFAULT VALUES") | ||||
|             .execute(db.deref_mut()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         result.last_insert_rowid() | ||||
|     } | ||||
|  | ||||
|     pub async fn insert(db: &SqlitePool) -> i64 { | ||||
|         let result: SqliteQueryResult = sqlx::query("INSERT INTO family DEFAULT VALUES") | ||||
|             .execute(db) | ||||
|   | ||||
| @@ -1,17 +1,18 @@ | ||||
| use std::ops::DerefMut; | ||||
|  | ||||
| use serde::Serialize; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; | ||||
|  | ||||
| #[derive(FromRow, Serialize, Clone)] | ||||
| #[derive(FromRow, Serialize, Clone, Deserialize, Debug)] | ||||
| pub struct Role { | ||||
|     pub(crate) id: i64, | ||||
|     pub(crate) name: String, | ||||
|     pub(crate) cluster: Option<String>, | ||||
| } | ||||
|  | ||||
| impl Role { | ||||
|     pub async fn all(db: &SqlitePool) -> Vec<Role> { | ||||
|         sqlx::query_as!(Role, "SELECT id, name FROM role") | ||||
|         sqlx::query_as!(Role, "SELECT id, name, cluster FROM role") | ||||
|             .fetch_all(db) | ||||
|             .await | ||||
|             .unwrap() | ||||
| @@ -21,7 +22,7 @@ impl Role { | ||||
|         sqlx::query_as!( | ||||
|             Self, | ||||
|             " | ||||
| SELECT id, name | ||||
| SELECT id, name, cluster | ||||
| FROM role  | ||||
| WHERE id like ? | ||||
|         ", | ||||
| @@ -31,12 +32,41 @@ WHERE id like ? | ||||
|         .await | ||||
|         .ok() | ||||
|     } | ||||
|     pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, name: i32) -> Option<Self> { | ||||
|         sqlx::query_as!( | ||||
|             Self, | ||||
|             " | ||||
| SELECT id, name, cluster | ||||
| FROM role  | ||||
| WHERE id like ? | ||||
|         ", | ||||
|             name | ||||
|         ) | ||||
|         .fetch_one(db.deref_mut()) | ||||
|         .await | ||||
|         .ok() | ||||
|     } | ||||
|  | ||||
|     pub async fn find_by_cluster_tx(db: &mut Transaction<'_, Sqlite>, name: i32) -> Option<Self> { | ||||
|         sqlx::query_as!( | ||||
|             Self, | ||||
|             " | ||||
| SELECT id, name, cluster | ||||
| FROM role  | ||||
| WHERE cluster = ? | ||||
|         ", | ||||
|             name | ||||
|         ) | ||||
|         .fetch_one(db.deref_mut()) | ||||
|         .await | ||||
|         .ok() | ||||
|     } | ||||
|  | ||||
|     pub async fn find_by_name(db: &SqlitePool, name: &str) -> Option<Self> { | ||||
|         sqlx::query_as!( | ||||
|             Self, | ||||
|             " | ||||
| SELECT id, name | ||||
| SELECT id, name, cluster | ||||
| FROM role  | ||||
| WHERE name like ? | ||||
|         ", | ||||
| @@ -51,7 +81,7 @@ WHERE name like ? | ||||
|         sqlx::query_as!( | ||||
|             Self, | ||||
|             " | ||||
| SELECT id, name | ||||
| SELECT id, name, cluster | ||||
| FROM role  | ||||
| WHERE name like ? | ||||
|         ", | ||||
|   | ||||
| @@ -490,6 +490,17 @@ ASKÖ Ruderverein Donau Linz", self.name), | ||||
|             _ => true, | ||||
|         } | ||||
|     } | ||||
|     pub async fn has_membership_pdf_tx(&self, db: &mut Transaction<'_, Sqlite>) -> bool { | ||||
|         match sqlx::query_scalar!("SELECT membership_pdf FROM user WHERE id = ?", self.id) | ||||
|             .fetch_one(db.deref_mut()) | ||||
|             .await | ||||
|             .unwrap() | ||||
|         { | ||||
|             Some(a) if a.is_empty() => false, | ||||
|             None => false, | ||||
|             _ => true, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn roles(&self, db: &SqlitePool) -> Vec<String> { | ||||
|         sqlx::query!( | ||||
| @@ -502,6 +513,21 @@ ASKÖ Ruderverein Donau Linz", self.name), | ||||
|         .into_iter().map(|r| r.name).collect() | ||||
|     } | ||||
|  | ||||
|     pub async fn real_roles(&self, db: &SqlitePool) -> Vec<Role> { | ||||
|         sqlx::query_as!( | ||||
|             Role, | ||||
|             "SELECT r.id, r.name, r.cluster  | ||||
|          FROM role r  | ||||
|          JOIN user_role ur ON r.id = ur.role_id  | ||||
|          JOIN user u ON u.id = ur.user_id  | ||||
|          WHERE ur.user_id = ? AND u.deleted = 0;", | ||||
|             self.id | ||||
|         ) | ||||
|         .fetch_all(db) | ||||
|         .await | ||||
|         .unwrap() | ||||
|     } | ||||
|  | ||||
|     pub async fn has_role_tx(&self, db: &mut Transaction<'_, Sqlite>, role: &str) -> bool { | ||||
|         if sqlx::query!( | ||||
|             "SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?)", | ||||
| @@ -697,14 +723,16 @@ ORDER BY last_access DESC | ||||
|             .is_ok() | ||||
|     } | ||||
|  | ||||
|     pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) { | ||||
|     pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) -> Result<(), String> { | ||||
|         let mut db = db.begin().await.map_err(|e| e.to_string())?; | ||||
|  | ||||
|         let mut family_id = data.family_id; | ||||
|  | ||||
|         if family_id.is_some_and(|x| x == -1) { | ||||
|             family_id = Some(Family::insert(db).await) | ||||
|             family_id = Some(Family::insert_tx(&mut db).await) | ||||
|         } | ||||
|  | ||||
|         if !self.has_membership_pdf(db).await { | ||||
|         if !self.has_membership_pdf_tx(&mut db).await { | ||||
|             if let Some(membership_pdf) = data.membership_pdf { | ||||
|                 let mut stream = membership_pdf.open().await.unwrap(); | ||||
|                 let mut buffer = Vec::new(); | ||||
| @@ -714,7 +742,7 @@ ORDER BY last_access DESC | ||||
|                     buffer, | ||||
|                     self.id | ||||
|                 ) | ||||
|                 .execute(db) | ||||
|                 .execute(db.deref_mut()) | ||||
|                 .await | ||||
|                 .unwrap(); //Okay, because we can only create a User of a valid id | ||||
|             } | ||||
| @@ -735,28 +763,29 @@ ORDER BY last_access DESC | ||||
|             family_id, | ||||
|             self.id | ||||
|         ) | ||||
|         .execute(db) | ||||
|         .execute(db.deref_mut()) | ||||
|         .await | ||||
|         .unwrap(); //Okay, because we can only create a User of a valid id | ||||
|  | ||||
|         // handle roles | ||||
|         sqlx::query!("DELETE FROM user_role WHERE user_id = ?", self.id) | ||||
|             .execute(db) | ||||
|             .execute(db.deref_mut()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         for role_id in data.roles.into_keys() { | ||||
|             self.add_role( | ||||
|                 db, | ||||
|                 &Role::find_by_id(db, role_id.parse::<i32>().unwrap()) | ||||
|                     .await | ||||
|                     .unwrap(), | ||||
|             ) | ||||
|             .await; | ||||
|             let role = Role::find_by_id_tx(&mut db, role_id.parse::<i32>().unwrap()) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|             self.add_role_tx(&mut db, &role).await?; | ||||
|         } | ||||
|  | ||||
|         db.commit().await.map_err(|e| e.to_string())?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn add_role(&self, db: &SqlitePool, role: &Role) { | ||||
|     pub async fn add_role(&self, db: &SqlitePool, role: &Role) -> Result<(), String> { | ||||
|         sqlx::query!( | ||||
|             "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)", | ||||
|             self.id, | ||||
| @@ -764,7 +793,40 @@ ORDER BY last_access DESC | ||||
|         ) | ||||
|         .execute(db) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|         .map_err(|_| { | ||||
|             format!( | ||||
|                 "User already has a role in the cluster '{}'", | ||||
|                 role.cluster | ||||
|                     .clone() | ||||
|                     .expect("db trigger can't activate on empty string") | ||||
|             ) | ||||
|         })?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn add_role_tx( | ||||
|         &self, | ||||
|         db: &mut Transaction<'_, Sqlite>, | ||||
|         role: &Role, | ||||
|     ) -> Result<(), String> { | ||||
|         sqlx::query!( | ||||
|             "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)", | ||||
|             self.id, | ||||
|             role.id | ||||
|         ) | ||||
|         .execute(db.deref_mut()) | ||||
|         .await | ||||
|         .map_err(|_| { | ||||
|             format!( | ||||
|                 "User already has a role in the cluster '{}'", | ||||
|                 role.cluster | ||||
|                     .clone() | ||||
|                     .expect("db trigger can't activate on empty string") | ||||
|             ) | ||||
|         })?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn remove_role(&self, db: &SqlitePool, role: &Role) { | ||||
| @@ -1234,7 +1296,8 @@ mod test { | ||||
|                 membership_pdf: None, | ||||
|             }, | ||||
|         ) | ||||
|         .await; | ||||
|         .await | ||||
|         .unwrap(); | ||||
|  | ||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||
|  | ||||
|   | ||||
| @@ -200,7 +200,8 @@ async fn fees_paid( | ||||
|             ) | ||||
|             .await; | ||||
|             user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap()) | ||||
|                 .await; | ||||
|                 .await | ||||
|                 .expect("paid role has no group"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -305,9 +306,10 @@ async fn update( | ||||
|         ); | ||||
|     }; | ||||
|  | ||||
|     user.update(db, data.into_inner()).await; | ||||
|  | ||||
|     Flash::success(Redirect::to("/admin/user"), "Successfully updated user") | ||||
|     match user.update(db, data.into_inner()).await { | ||||
|         Ok(_) => Flash::success(Redirect::to("/admin/user"), "Successfully updated user"), | ||||
|         Err(e) => Flash::error(Redirect::to("/admin/user"), e), | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[get("/user/<user>/membership")] | ||||
| @@ -394,7 +396,9 @@ async fn create_scheckbuch( | ||||
|  | ||||
|     // 4. Add 'scheckbuch' role | ||||
|     let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap(); | ||||
|     user.add_role(db, &scheckbuch).await; | ||||
|     user.add_role(db, &scheckbuch) | ||||
|         .await | ||||
|         .expect("new user has no roles yet"); | ||||
|  | ||||
|     // 4. Send welcome mail (+ notification) | ||||
|     user.send_welcome_email(db, &config.smtp_pw).await.unwrap(); | ||||
| @@ -434,10 +438,14 @@ async fn schnupper_to_scheckbuch( | ||||
|     user.remove_role(db, &paid).await; | ||||
|  | ||||
|     let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap(); | ||||
|     user.add_role(db, &scheckbuch).await; | ||||
|     user.add_role(db, &scheckbuch) | ||||
|         .await | ||||
|         .expect("just removed 'schnupperant' thus can't have a role with that group"); | ||||
|  | ||||
|     if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await { | ||||
|         user.add_role(db, &no_einschreibgebuehr).await; | ||||
|         user.add_role(db, &no_einschreibgebuehr) | ||||
|             .await | ||||
|             .expect("role doesn't have a group"); | ||||
|     } | ||||
|  | ||||
|     user.send_welcome_email(db, &config.smtp_pw).await.unwrap(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user