diff --git a/src/endpoints/mod.rs b/src/endpoints/mod.rs
index a9def783..0b25f6ea 100644
--- a/src/endpoints/mod.rs
+++ b/src/endpoints/mod.rs
@@ -37,6 +37,10 @@ pub fn init(cfg: &mut ServiceConfig) {
cfg.service(user::post_login::post);
cfg.service(user::get_reset::get);
cfg.service(user::post_reset::post);
+ cfg.service(user::get_profile::get);
+ cfg.service(user::post_toggle::post);
+ cfg.service(user::get_changepassword::get);
+ cfg.service(user::post_changepassword::post);
cfg.service(availability::delete::delete);
cfg.service(availability::get_new::get);
diff --git a/src/endpoints/user/get_changepassword.rs b/src/endpoints/user/get_changepassword.rs
new file mode 100644
index 00000000..826e3fa7
--- /dev/null
+++ b/src/endpoints/user/get_changepassword.rs
@@ -0,0 +1,45 @@
+use actix_web::{HttpRequest, HttpResponse, Responder};
+
+#[actix_web::get("/users/changepassword")]
+pub async fn get(request: HttpRequest) -> impl Responder {
+ if let Some(_) = request.headers().get("HX-Request") {
+ return HttpResponse::Ok().body(r##"
+
+
+ "##);
+ }
+
+ HttpResponse::NotFound().finish()
+}
diff --git a/src/endpoints/user/get_profile.rs b/src/endpoints/user/get_profile.rs
new file mode 100644
index 00000000..d1b44297
--- /dev/null
+++ b/src/endpoints/user/get_profile.rs
@@ -0,0 +1,24 @@
+use actix_web::{web, Responder};
+use askama::Template;
+use askama_actix::TemplateToResponse;
+use sqlx::PgPool;
+
+use crate::models::{Area, Function, Role, User};
+
+#[derive(Template)]
+#[template(path = "user/profile.html")]
+struct ProfileTemplate {
+ user: User
+}
+
+#[actix_web::get("/profile")]
+pub async fn get(user: web::ReqData, pool: web::Data) -> impl Responder {
+ let area = Area::read_by_id(pool.get_ref(), user.area_id).await.unwrap();
+
+ let mut user = user.into_inner();
+ user.area = Some(area);
+
+ let template = ProfileTemplate { user };
+
+ return template.to_response();
+}
diff --git a/src/endpoints/user/mod.rs b/src/endpoints/user/mod.rs
index 69ee17f7..8303493a 100644
--- a/src/endpoints/user/mod.rs
+++ b/src/endpoints/user/mod.rs
@@ -10,3 +10,7 @@ pub mod get_login;
pub mod post_login;
pub mod get_reset;
pub mod post_reset;
+pub mod get_profile;
+pub mod post_toggle;
+pub mod get_changepassword;
+pub mod post_changepassword;
diff --git a/src/endpoints/user/patch.rs b/src/endpoints/user/patch.rs
index a7dc0788..5586d363 100644
--- a/src/endpoints/user/patch.rs
+++ b/src/endpoints/user/patch.rs
@@ -13,32 +13,28 @@ use crate::{
pub struct JsonPatchDoc {
op: String,
path: String,
- value: Value
+ value: Value,
}
#[actix_web::patch("/users/edit/{id}")]
pub async fn patch(
- user: Identity,
+ user: web::ReqData,
pool: web::Data,
path: web::Path,
patch_docs: web::Json>,
) -> impl Responder {
- let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap())
- .await
- .unwrap();
- if current_user.role != Role::AreaManager && current_user.role != Role::Admin {
- return HttpResponse::Unauthorized().finish();
- }
+ let is_superuser = user.role != Role::AreaManager && user.role != Role::Admin;
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
- if current_user.role == Role::AreaManager && current_user.area_id != user_in_db.area_id {
+ if user.role == Role::AreaManager && user.area_id != user_in_db.area_id {
return HttpResponse::Unauthorized().finish();
}
let mut changed = false;
let mut locked: Option = None;
+ let mut receive_notifications: Option = None;
for doc in patch_docs.iter() {
if doc.op.as_str() != "replace" {
@@ -47,21 +43,44 @@ pub async fn patch(
match doc.path.as_str() {
"/locked" => {
+ if !is_superuser {
+ return HttpResponse::Unauthorized().finish();
+ }
changed = true;
if let Value::Bool(b) = doc.value {
locked = Some(b);
}
- },
- _ => panic!("other patch paths are not supported!"),
+ }
+ "/receiveNotifications" => {
+ changed = true;
+ if let Value::Bool(b) = doc.value {
+ receive_notifications = Some(b)
+ }
+ }
+ _ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!")
};
}
if changed {
- if let Ok(_) = User::update(pool.get_ref(), path.id, None, None, None, None, None, None, None, locked).await {
+ if let Ok(_) = User::update(
+ pool.get_ref(),
+ path.id,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ receive_notifications,
+ locked,
+ )
+ .await
+ {
return HttpResponse::Ok().body("");
}
} else {
- return HttpResponse::Ok().body("");
+ return HttpResponse::Ok().body("");
}
}
diff --git a/src/endpoints/user/post_changepassword.rs b/src/endpoints/user/post_changepassword.rs
new file mode 100644
index 00000000..ddba251b
--- /dev/null
+++ b/src/endpoints/user/post_changepassword.rs
@@ -0,0 +1,49 @@
+use actix_web::{web, HttpResponse, Responder};
+use serde::Deserialize;
+use sqlx::PgPool;
+
+use crate::{auth::utils, models::User};
+
+#[derive(Deserialize)]
+struct ChangePasswordForm {
+ currentpassword: String,
+ password: String,
+ passwordretyped: String,
+}
+
+#[actix_web::post("/users/changepassword")]
+async fn post(
+ user: web::ReqData,
+ form: web::Form,
+ pool: web::Data,
+) -> impl Responder {
+ if user.password
+ == utils::hash_plain_password_with_salt(&form.currentpassword, &user.salt).unwrap()
+ {
+ if form.password != form.passwordretyped {
+ return HttpResponse::BadRequest().body("Passwörter stimmen nicht überein!");
+ }
+
+ let (hash, salt) = utils::generate_salt_and_hash_plain_password(&form.password).unwrap();
+
+ User::update(
+ pool.get_ref(),
+ user.id,
+ None,
+ None,
+ Some(&hash),
+ Some(&salt),
+ None,
+ None,
+ None,
+ None,
+ None,
+ )
+ .await
+ .unwrap();
+
+ return HttpResponse::Ok().body(r#"Passwort wurde geändert.
"#);
+ } else {
+ return HttpResponse::BadRequest().body("Aktuelles Passwort ist nicht korrekt!");
+ }
+}
diff --git a/src/endpoints/user/post_edit.rs b/src/endpoints/user/post_edit.rs
index 63605cf5..583a1c30 100644
--- a/src/endpoints/user/post_edit.rs
+++ b/src/endpoints/user/post_edit.rs
@@ -83,7 +83,7 @@ pub async fn post_edit(
};
if changed {
- match User::update(pool.get_ref(), path.id, email, name, None, None, role, function, area, None).await {
+ match User::update(pool.get_ref(), path.id, email, name, None, None, role, function, area, None, None).await {
Ok(_) => return HttpResponse::Found().insert_header((LOCATION, "/users")).finish(),
Err(err) => println!("{}", err)
}
diff --git a/src/endpoints/user/post_reset.rs b/src/endpoints/user/post_reset.rs
index cdb2552c..612c9f4f 100644
--- a/src/endpoints/user/post_reset.rs
+++ b/src/endpoints/user/post_reset.rs
@@ -91,6 +91,7 @@ async fn post(form: web::Form, pool: web::Data) -> im
None,
None,
None,
+ None
)
.await
.unwrap();
diff --git a/src/endpoints/user/post_toggle.rs b/src/endpoints/user/post_toggle.rs
new file mode 100644
index 00000000..b91c0782
--- /dev/null
+++ b/src/endpoints/user/post_toggle.rs
@@ -0,0 +1,70 @@
+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,
+ pool: web::Data,
+ path: web::Path,
+ query: web::Query,
+) -> impl Responder {
+ 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()
+ } else {
+ user.into_inner()
+ };
+
+ match query.field.as_str() {
+ "locked" => User::update(
+ pool.get_ref(),
+ user.id,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(!user.locked),
+ )
+ .await
+ .unwrap(),
+ "receiveNotifications" => {
+ User::update(
+ pool.get_ref(),
+ user.id,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(!user.receive_notifications),
+ None,
+ )
+ .await
+ .unwrap();
+ //return HttpResponse::Ok().body("");
+ }
+ _ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!"),
+ };
+
+ HttpResponse::Ok().finish()
+}
diff --git a/src/models/user.rs b/src/models/user.rs
index 3154e5ee..b48dc606 100644
--- a/src/models/user.rs
+++ b/src/models/user.rs
@@ -66,7 +66,7 @@ impl User {
areaId,
locked,
lastLogin,
- receiveNotifications
+ receiveNotifications
FROM user_
WHERE id = $1;
"#,
@@ -106,7 +106,7 @@ impl User {
areaId,
locked,
lastLogin,
- receiveNotifications
+ receiveNotifications
FROM user_
WHERE email = $1 AND locked = FALSE;
"#,
@@ -146,7 +146,7 @@ impl User {
areaId,
locked,
lastLogin,
- receiveNotifications
+ receiveNotifications
FROM user_;
"#,
)
@@ -235,7 +235,7 @@ impl User {
areaId,
locked,
lastLogin,
- receiveNotifications
+ receiveNotifications
FROM user_
WHERE areaId = $1;
"#,
@@ -275,6 +275,7 @@ impl User {
role: Option,
function: Option,
area_id: Option,
+ receive_notifications: Option,
locked: Option,
) -> anyhow::Result<()> {
let mut query_builder = sqlx::QueryBuilder::new("UPDATE user_ SET ");
@@ -315,6 +316,11 @@ impl User {
separated.push_bind_unseparated(area_id);
}
+ if let Some(receive_notifications) = receive_notifications {
+ separated.push("receiveNotifications = ");
+ separated.push_bind_unseparated(receive_notifications);
+ }
+
if let Some(locked) = locked {
separated.push("locked = ");
separated.push_bind_unseparated(locked);
diff --git a/templates/base.html b/templates/base.html
index bba187b7..a0e8ed8b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -33,6 +33,16 @@
top: -.05em;
vertical-align: middle;
}
+
+ .result {
+ visibility: hidden;
+ }
+
+ .fadeout {
+ visibility: visible;
+ opacity: 0;
+ transition: opacity 2s ease-in;
+ }
diff --git a/templates/nav.html b/templates/nav.html
index ddc778c1..91b81df4 100644
--- a/templates/nav.html
+++ b/templates/nav.html
@@ -66,10 +66,10 @@
angemeldet als {{ user.name }}