use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; use garde::Validate; use sqlx::PgPool; use crate::{ endpoints::{user::NewOrEditUserForm, IdPath}, models::{Area, Function, Role, User, UserChangeset}, utils::ApplicationError, }; #[actix_web::post("/users/edit/{id}")] pub async fn post_edit( user: web::ReqData, pool: web::Data, path: web::Path, form: web::Form, ) -> Result { if user.role != Role::AreaManager && user.role != Role::Admin { return Err(ApplicationError::Unauthorized); } if user.id == path.id { return Ok(HttpResponse::BadRequest().finish()); } let Some(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await? else { return Ok(HttpResponse::NotFound().finish()); }; if user.role == Role::AreaManager && user.area_id != user_in_db.area_id { return Err(ApplicationError::Unauthorized); } let area_id = form.area.unwrap_or(user_in_db.area_id); if Area::read_by_id(pool.get_ref(), area_id).await?.is_none() { return Err(ApplicationError::Unauthorized); } let mut functions = Vec::with_capacity(3); if form.is_posten.unwrap_or(false) { functions.push(Function::Posten); } if form.is_wachhabender.unwrap_or(false) { functions.push(Function::Wachhabender); } if form.is_fuehrungsassistent.unwrap_or(false) { functions.push(Function::Fuehrungsassistent); } let changeset = UserChangeset { name: form.name.clone(), email: form.email.clone(), role: form.role.try_into()?, functions, area_id, }; if let Err(e) = changeset.validate() { return Ok(HttpResponse::BadRequest().body(e.to_string())); }; User::update(pool.get_ref(), user_in_db.id, changeset).await?; Ok(HttpResponse::Found() .insert_header((LOCATION, "/users")) .insert_header(("HX-LOCATION", "/users")) .finish()) } #[cfg(test)] mod tests { use crate::{endpoints::user::NewOrEditUserForm, models::*, utils::test_helper::*}; use brass_macros::db_test; use fake::{ faker::{internet::en::SafeEmail, name::en::Name}, Fake, Faker, }; #[db_test] async fn works_when_user_is_admin(context: &DbTestContext) { User::create(&context.db_pool, Faker.fake()).await.unwrap(); Area::create(&context.db_pool, "Süd").await.unwrap(); let app = context.app().await; let config = RequestConfig { uri: "/users/edit/1".to_string(), role: Role::Admin, function: vec![Function::Posten], user_area: 1, }; let new_name: String = Name().fake(); let new_mail: String = SafeEmail().fake(); let form = NewOrEditUserForm { name: new_name.clone(), email: new_mail.clone(), role: Role::AreaManager as u8, is_posten: None, is_wachhabender: None, is_fuehrungsassistent: Some(true), area: Some(2), }; let response = test_post(&context.db_pool, app, &config, form).await; assert_eq!(StatusCode::FOUND, response.status()); let updated_user = User::read_by_id(&context.db_pool, 1) .await .unwrap() .unwrap(); assert_eq!(new_name, updated_user.name); assert_eq!(new_mail, updated_user.email); assert_eq!(Role::AreaManager, updated_user.role); assert!(updated_user.function.is_fuehrungsassistent()); assert_eq!(2, updated_user.area_id); } #[db_test] async fn cant_edit_oneself(context: &DbTestContext) { let app = context.app().await; let config = RequestConfig { uri: "/users/edit/1".to_string(), role: Role::Admin, function: vec![Function::Posten], user_area: 1, }; let form = NewOrEditUserForm { name: "".to_string(), email: "".to_string(), role: Role::AreaManager as u8, is_posten: None, is_wachhabender: None, is_fuehrungsassistent: Some(true), area: Some(1), }; let response = test_post(&context.db_pool, app, &config, form).await; assert_eq!(StatusCode::BAD_REQUEST, response.status()); } }