brass/web/src/endpoints/user/put_lock.rs
2025-04-27 20:38:02 +02:00

182 lines
5.2 KiB
Rust

use actix_web::{web, HttpResponse, Responder};
use askama::Template;
use sqlx::PgPool;
use crate::{
endpoints::IdPath,
filters,
models::{Role, User},
utils::ApplicationError,
};
#[derive(Template, Debug)]
#[template(path = "user/overview_locked_td.html")]
struct OverviewPartialLockedTemplate {
u: User,
is_oob: bool,
}
#[derive(Template, Debug)]
#[template(path = "user/overview_buttons_td.html")]
struct OverviewPartialButtonsTemplate {
u: User,
}
#[actix_web::put("/users/{id}/lock")]
pub async fn put_lock_user(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
path: web::Path<IdPath>,
) -> Result<impl Responder, ApplicationError> {
handle_lock_state_for_user(user, pool, path, true).await
}
#[actix_web::put("/users/{id}/unlock")]
pub async fn put_unlock_user(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
path: web::Path<IdPath>,
) -> Result<impl Responder, ApplicationError> {
handle_lock_state_for_user(user, pool, path, false).await
}
async fn handle_lock_state_for_user(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
path: web::Path<IdPath>,
lock_state: bool,
) -> Result<impl Responder, ApplicationError> {
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(mut 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 || user_in_db.role == Role::Admin)
{
return Err(ApplicationError::Unauthorized);
}
if user_in_db.locked != lock_state {
User::update_locked(pool.get_ref(), user_in_db.id, lock_state).await?;
user_in_db.locked = lock_state;
}
let buttons = OverviewPartialButtonsTemplate {
u: user_in_db.clone(),
};
let locked_oob = OverviewPartialLockedTemplate {
u: user_in_db,
is_oob: true,
};
let mut body = buttons.render()?;
body.push_str(&locked_oob.render()?);
Ok(HttpResponse::Ok().body(body))
}
#[cfg(test)]
mod tests {
use crate::{
models::{Area, Function, Role, User},
utils::test_helper::{
assert_snapshot, read_body, test_put, DbTestContext, RequestConfig, StatusCode,
},
};
use brass_macros::db_test;
use fake::{Fake, Faker};
#[db_test]
async fn admin_can_lock_and_unlock_user(context: &DbTestContext) {
let app = context.app().await;
User::create(&context.db_pool, Faker.fake()).await.unwrap();
let lock_config = RequestConfig {
uri: "/users/1/lock".to_string(),
role: Role::Admin,
function: vec![Function::Posten],
user_area: 1,
};
let lock_response =
test_put::<_, _, String>(&context.db_pool, &app, &lock_config, None).await;
assert_eq!(StatusCode::OK, lock_response.status());
let lock_body = read_body(lock_response).await;
assert_snapshot!(lock_body);
let unlock_config = RequestConfig {
uri: "/users/1/unlock".to_string(),
role: Role::Admin,
function: vec![Function::Posten],
user_area: 1,
};
let unlock_response =
test_put::<_, _, String>(&context.db_pool, &app, &unlock_config, None).await;
assert_eq!(StatusCode::OK, unlock_response.status());
let unlock_body = read_body(unlock_response).await;
assert_snapshot!(unlock_body);
}
#[db_test]
async fn area_manager_cant_lock_outside_of_his_area(context: &DbTestContext) {
let app = context.app().await;
Area::create(&context.db_pool, "Bereich 2").await.unwrap();
User::create(&context.db_pool, Faker.fake()).await.unwrap();
let config = RequestConfig {
uri: "/users/1/lock".to_string(),
role: Role::AreaManager,
function: vec![Function::Posten],
user_area: 2,
};
let response = test_put::<_, _, String>(&context.db_pool, &app, &config, None).await;
assert_eq!(StatusCode::UNAUTHORIZED, response.status())
}
#[db_test]
async fn one_cant_lock_oneself(context: &DbTestContext) {
let app = context.app().await;
let config = RequestConfig {
uri: "/users/1/lock".to_string(),
role: Role::Admin,
function: vec![Function::Posten],
user_area: 1,
};
let response = test_put::<_, _, String>(&context.db_pool, &app, &config, None).await;
assert_eq!(StatusCode::BAD_REQUEST, response.status())
}
#[db_test]
async fn one_cant_lock_non_existing_user(context: &DbTestContext) {
let app = context.app().await;
let config = RequestConfig {
uri: "/users/30/lock".to_string(),
role: Role::Admin,
function: vec![Function::Posten],
user_area: 1,
};
let response = test_put::<_, _, String>(&context.db_pool, &app, &config, None).await;
assert_eq!(StatusCode::NOT_FOUND, response.status())
}
}