parent
5446fcb128
commit
08646ba971
@ -43,7 +43,6 @@ pub fn init(cfg: &mut ServiceConfig) {
|
|||||||
cfg.service(user::get_reset::get);
|
cfg.service(user::get_reset::get);
|
||||||
cfg.service(user::post_reset::post);
|
cfg.service(user::post_reset::post);
|
||||||
cfg.service(user::get_profile::get);
|
cfg.service(user::get_profile::get);
|
||||||
cfg.service(user::post_toggle::post);
|
|
||||||
cfg.service(user::get_changepassword::get);
|
cfg.service(user::get_changepassword::get);
|
||||||
cfg.service(user::post_changepassword::post);
|
cfg.service(user::post_changepassword::post);
|
||||||
cfg.service(user::get_register::get);
|
cfg.service(user::get_register::get);
|
||||||
@ -51,6 +50,8 @@ pub fn init(cfg: &mut ServiceConfig) {
|
|||||||
cfg.service(user::post_resend_registration::post);
|
cfg.service(user::post_resend_registration::post);
|
||||||
cfg.service(user::put_receive_notifications::put_subscribe);
|
cfg.service(user::put_receive_notifications::put_subscribe);
|
||||||
cfg.service(user::put_receive_notifications::put_unsubscribe);
|
cfg.service(user::put_receive_notifications::put_unsubscribe);
|
||||||
|
cfg.service(user::put_lock::put_lock_user);
|
||||||
|
cfg.service(user::put_lock::put_unlock_user);
|
||||||
|
|
||||||
cfg.service(availability::delete::delete);
|
cfg.service(availability::delete::delete);
|
||||||
cfg.service(availability::get_new::get);
|
cfg.service(availability::get_new::get);
|
||||||
|
@ -22,7 +22,8 @@ pub mod post_new;
|
|||||||
pub mod post_register;
|
pub mod post_register;
|
||||||
pub mod post_resend_registration;
|
pub mod post_resend_registration;
|
||||||
pub mod post_reset;
|
pub mod post_reset;
|
||||||
pub mod post_toggle;
|
pub mod put_receive_notifications;
|
||||||
|
pub mod put_lock;
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "user/new_or_edit.html")]
|
#[template(path = "user/new_or_edit.html")]
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use sqlx::PgPool;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
endpoints::IdPath,
|
|
||||||
models::{Role, User},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ToggleQuery {
|
|
||||||
field: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::post("/users/{id}/toggle")]
|
|
||||||
pub async fn post(
|
|
||||||
user: web::ReqData<User>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
path: web::Path<IdPath>,
|
|
||||||
query: web::Query<ToggleQuery>,
|
|
||||||
) -> impl Responder {
|
|
||||||
// Todo: rewrite
|
|
||||||
if user.id != path.id && user.role != Role::Admin && user.role != Role::AreaManager {
|
|
||||||
return HttpResponse::Unauthorized().finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
let user = if user.id != path.id {
|
|
||||||
User::read_by_id(pool.get_ref(), path.id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
} else {
|
|
||||||
user.into_inner()
|
|
||||||
};
|
|
||||||
|
|
||||||
match query.field.as_str() {
|
|
||||||
"locked" => {
|
|
||||||
User::update_locked(pool.get_ref(), user.id, !user.locked)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if !user.locked {
|
|
||||||
return HttpResponse::Ok().body(format!(
|
|
||||||
r##"<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#unlock" />
|
|
||||||
</svg>
|
|
||||||
<span>Entsperren</span>
|
|
||||||
<div id="user-{id}-locked" hx-swap-oob="true">ja</div>
|
|
||||||
<button id="user-{id}-delete" hx-swap-oob="true" class="button is-danger is-light" hx-delete="/users/{id}" hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#x-circle" />
|
|
||||||
</svg>
|
|
||||||
<span>Löschen</span>
|
|
||||||
</button>"##,
|
|
||||||
id = user.id));
|
|
||||||
} else {
|
|
||||||
return HttpResponse::Ok().body(format!(
|
|
||||||
r##"<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#lock" />
|
|
||||||
</svg>
|
|
||||||
<span>Sperren</span>
|
|
||||||
<div id="user-{id}-locked" hx-swap-oob="true">nein</div>
|
|
||||||
<button id="user-{id}-delete" hx-swap-oob="true" class="button is-danger is-light" disabled hx-delete="/users/{id}" hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#x-circle" />
|
|
||||||
</svg>
|
|
||||||
<span>Löschen</span>
|
|
||||||
</button>"##,
|
|
||||||
id = user.id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!"),
|
|
||||||
};
|
|
||||||
}
|
|
128
web/src/endpoints/user/put_lock.rs
Normal file
128
web/src/endpoints/user/put_lock.rs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Tests schreiben
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use crate::utils::test_helper::{
|
||||||
|
// assert_snapshot, read_body, test_put, DbTestContext, RequestConfig, StatusCode,
|
||||||
|
// };
|
||||||
|
// use brass_macros::db_test;
|
||||||
|
//
|
||||||
|
// #[db_test]
|
||||||
|
// async fn user_can_toggle_subscription_for_himself(context: &DbTestContext) {
|
||||||
|
// let app = context.app().await;
|
||||||
|
//
|
||||||
|
// let unsubscribe_config = RequestConfig::new("/users/1/unsubscribeNotifications");
|
||||||
|
// let unsubscribe_response =
|
||||||
|
// test_put::<_, _, String>(&context.db_pool, &app, &unsubscribe_config, None).await;
|
||||||
|
//
|
||||||
|
// assert_eq!(StatusCode::OK, unsubscribe_response.status());
|
||||||
|
//
|
||||||
|
// let unsubscribe_body = read_body(unsubscribe_response).await;
|
||||||
|
// assert_snapshot!(unsubscribe_body);
|
||||||
|
//
|
||||||
|
// let subscribe_config = RequestConfig::new("/users/1/subscribeNotifications");
|
||||||
|
// let subscribe_response =
|
||||||
|
// test_put::<_, _, String>(&context.db_pool, &app, &subscribe_config, None).await;
|
||||||
|
//
|
||||||
|
// assert_eq!(StatusCode::OK, subscribe_response.status());
|
||||||
|
//
|
||||||
|
// let subscribe_body = read_body(subscribe_response).await;
|
||||||
|
// assert_snapshot!(subscribe_body);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[db_test]
|
||||||
|
// async fn user_cant_toggle_subscription_for_someone_else(context: &DbTestContext) {
|
||||||
|
// let app = context.app().await;
|
||||||
|
//
|
||||||
|
// let unsubscribe_config = RequestConfig::new("/users/3/unsubscribeNotifications");
|
||||||
|
// let unsubscribe_response =
|
||||||
|
// test_put::<_, _, String>(&context.db_pool, &app, &unsubscribe_config, None).await;
|
||||||
|
//
|
||||||
|
// assert_eq!(StatusCode::UNAUTHORIZED, unsubscribe_response.status());
|
||||||
|
// }
|
||||||
|
// }
|
@ -72,44 +72,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div id="user-{{ u.id }}-locked">
|
{% include "overview_locked_td.html" %}
|
||||||
{% if u.locked %}
|
|
||||||
ja
|
|
||||||
{% else %}
|
|
||||||
nein
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if user.id != u.id %}
|
{% let current_user_is_area_manager_and_user_is_not_admin = user.role == Role::AreaManager && u.role !=
|
||||||
|
Role::Admin %}
|
||||||
|
{% if user.id != u.id && (user.role == Role::Admin || current_user_is_area_manager_and_user_is_not_admin)
|
||||||
|
%}
|
||||||
<div class="buttons is-right">
|
<div class="buttons is-right">
|
||||||
<button class="button is-link is-light" hx-post="/users/{{ u.id }}/toggle?field=locked">
|
{% include "overview_buttons_td.html" %}
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#{% if u.locked %}unlock{% else %}lock{% endif %}" />
|
|
||||||
</svg>
|
|
||||||
<span>{% if u.locked %}Entsperren{% else %}Sperren{% endif %}</span>
|
|
||||||
</button>
|
|
||||||
<a class="button is-primary is-light" hx-boost="true" href="/users/edit/{{ u.id }}">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#edit" />
|
|
||||||
</svg>
|
|
||||||
<span>Bearbeiten</span>
|
|
||||||
</a>
|
|
||||||
<button id="user-{{ u.id }}-delete" class="button is-danger is-light" {% if !u.locked %}disabled{% endif
|
|
||||||
%} hx-delete="/users/{{ u.id }}" hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#x-circle" />
|
|
||||||
</svg>
|
|
||||||
<span>Löschen</span>
|
|
||||||
</button>
|
|
||||||
{% if u.password.is_none() && u.salt.is_none() && u.last_login.is_none() %}
|
|
||||||
<button class="button is-warning is-light" hx-post="/users/{{ u.id }}/resend-registration">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#send" />
|
|
||||||
</svg>
|
|
||||||
<span>Registrierungsmail erneut senden</span>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
31
web/templates/user/overview_buttons_td.html
Normal file
31
web/templates/user/overview_buttons_td.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<button class="button is-link is-light" hx-put="/users/{{ u.id }}/{%- if u.locked -%}un{%- endif -%}lock"
|
||||||
|
hx-target="closest div" hx-swap="innerHTML">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#{%- if u.locked -%}un{%- endif -%}lock" />
|
||||||
|
</svg>
|
||||||
|
<span>{%- if u.locked -%}Entsperren{%- else -%}Sperren{%- endif -%}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="button is-primary is-light" hx-boost="true" href="/users/edit/{{ u.id }}">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#edit" />
|
||||||
|
</svg>
|
||||||
|
<span>Bearbeiten</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button id="user-{{ u.id }}-delete" class="button is-danger is-light" {{ !u.locked|cond_show("disabled") }}
|
||||||
|
hx-delete="/users/{{ u.id }}" hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#x-circle" />
|
||||||
|
</svg>
|
||||||
|
<span>Löschen</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{% if u.password.is_none() && u.salt.is_none() && u.last_login.is_none() %}
|
||||||
|
<button class="button is-warning is-light" hx-post="/users/{{ u.id }}/resend-registration">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#send" />
|
||||||
|
</svg>
|
||||||
|
<span>Registrierungsmail erneut senden</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
7
web/templates/user/overview_locked_td.html
Normal file
7
web/templates/user/overview_locked_td.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<div id="user-{{ u.id }}-locked" {% if is_oob -%}hx-swap-oob="true" {%- endif -%}>
|
||||||
|
{%- if u.locked -%}
|
||||||
|
ja
|
||||||
|
{%- else -%}
|
||||||
|
nein
|
||||||
|
{%- endif -%}
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user