From f361a13c91844b26e5c5a0c6d504f3eef7a119b8 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 15 Jun 2021 12:59:56 +0200 Subject: [PATCH] make privileges more defining for UI --- app/src/lib.rs | 8 +-- app/src/navigation.rs | 12 ++-- app/src/pages/list_links.rs | 76 ++++++++++++------------- app/src/pages/list_users.rs | 110 +++++++++++++++++++++++++++--------- locales/de/main.ftl | 2 + locales/en/main.ftl | 2 + pslink/src/lib.rs | 3 +- pslink/src/queries.rs | 10 +++- pslink/src/views.rs | 14 +++-- 9 files changed, 150 insertions(+), 87 deletions(-) diff --git a/app/src/lib.rs b/app/src/lib.rs index 74d6b37..013e681 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -335,7 +335,7 @@ fn view(model: &Model) -> Node { match model.user { Loadable::Data(Some(ref user)) => div![ navigation::navigation(&model.i18n, &model.location.base_url, user), - view_content(&model.page, &model.location.base_url) + view_content(&model.page, &model.location.base_url, user) ], Loadable::Data(None) => view_login(&model.i18n, model), Loadable::Loading => div![C!("lds-ellipsis"), div!(), div!(), div!(), div!()], @@ -344,12 +344,12 @@ fn view(model: &Model) -> Node { } /// Render the subpages. -fn view_content(page: &Page, url: &Url) -> Node { +fn view_content(page: &Page, url: &Url, user: &User) -> Node { div![ C!["container"], match page { - Page::Home(model) => pages::list_links::view(model).map_msg(Msg::ListLinks), - Page::ListUsers(model) => pages::list_users::view(model).map_msg(Msg::ListUsers), + Page::Home(model) => pages::list_links::view(model, user).map_msg(Msg::ListLinks), + Page::ListUsers(model) => pages::list_users::view(model, user).map_msg(Msg::ListUsers), Page::NotFound => div![div![url.to_string()], "Page not found!"], } ] diff --git a/app/src/navigation.rs b/app/src/navigation.rs index 8b1478c..d7df64c 100644 --- a/app/src/navigation.rs +++ b/app/src/navigation.rs @@ -47,16 +47,16 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node { ) )), t("invite-user"), - ],], - // A button to list all users - li![a![ - attrs! {At::Href => crate::Urls::new(base_url).list_users()}, - t("list-users"), - ],], + ],] ] } else { nodes!() }, + // A button to list all users + li![a![ + attrs! {At::Href => crate::Urls::new(base_url).list_users()}, + t("list-users"), + ],], ], ol![ li![div![ diff --git a/app/src/pages/list_links.rs b/app/src/pages/list_links.rs index 6209b22..cfff6bf 100644 --- a/app/src/pages/list_links.rs +++ b/app/src/pages/list_links.rs @@ -13,7 +13,7 @@ use shared::{ general::{EditMode, Message, Operation, Status}, links::{LinkDelta, LinkOverviewColumns, LinkRequestForm}, }, - datatypes::{FullLink, Lang, Loadable}, + datatypes::{FullLink, Lang, Loadable, User}, }; use crate::{get_host, i18n::I18n, unwrap_or_return}; @@ -456,7 +456,7 @@ fn delete_link(link_delta: LinkDelta, orders: &mut impl Orders) { /// * questions /// * the table of links including sorting and searching #[must_use] -pub fn view(model: &Model) -> Node { +pub fn view(model: &Model, logged_in_user: &User) -> Node { let lang = &model.i18n.clone(); // shortcut for translating let t = move |key: &str| lang.translate(key, None); @@ -498,7 +498,7 @@ pub fn view(model: &Model) -> Node { // Add filter fields right below the headlines view_link_table_filter_input(model, &t), // Add all the content lines - model.links.iter().map(view_link) + model.links.iter().map(|l| { view_link(l, logged_in_user) }) ], // A fetch button - this should not be needed and will be removed in future. button![ @@ -541,6 +541,7 @@ fn view_link_table_head String>(t: F) -> Node { ))), t("statistics") ], + th![], th![] ] } @@ -593,43 +594,34 @@ fn view_link_table_filter_input String>(model: &Model, t: F) -> N // statistics and the delete column cannot be filtered td![], td![], + td![], ] } /// display a single table row containing one link -fn view_link(l: &FullLink) -> Node { - // Ugly hack - this is needed to be able to move the l into the closures... l.clone() in place does not work. - let link = LinkDelta::from(l.clone()); - let link2 = LinkDelta::from(l.clone()); - let link3 = LinkDelta::from(l.clone()); - let link4 = LinkDelta::from(l.clone()); - let link5 = LinkDelta::from(l.clone()); +fn view_link(l: &FullLink, logged_in_user: &User) -> Node { + use shared::apirequests::users::Role; + macro_rules! event_or_not { + ( $link:expr, $user:expr, $content:expr) => { + if $user.role == Role::Admin + || ($user.role == Role::Regular) && $link.user.id == $user.id + { + let link = LinkDelta::from($link.clone()); + td![ + ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link))), + $content + ] + } else { + td![$content] + } + }; + } tr![ - { - td![ - ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link))), - &l.link.code - ] - }, - { - td![ - ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link2))), - &l.link.title - ] - }, - td![a![attrs![At::Href => &l.link.target], &l.link.target]], - { - td![ - ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link3))), - &l.user.username - ] - }, - { - td![ - ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link4))), - &l.clicks.number - ] - }, + event_or_not!(l, logged_in_user, &l.link.code), + event_or_not!(l, logged_in_user, &l.link.title), + event_or_not!(l, logged_in_user, &l.link.target), + event_or_not!(l, logged_in_user, &l.user.username), + event_or_not!(l, logged_in_user, &l.clicks.number), { td![ C!["table_qr"], @@ -639,12 +631,16 @@ fn view_link(l: &FullLink) -> Node { ] ] }, + if logged_in_user.role == Role::Admin + || (logged_in_user.role == Role::Regular) && l.user.id == logged_in_user.id { - td![img![ - ev(Ev::Click, |_| Msg::Edit(EditMsg::MayDeleteSelected(link5))), - C!["trashicon"], - attrs!(At::Src => "/static/trash.svg") - ]] + let link = LinkDelta::from(l.clone()); + td![ + ev(Ev::Click, |_| Msg::Edit(EditMsg::MayDeleteSelected(link))), + img![C!["trashicon"], attrs!(At::Src => "/static/trash.svg")] + ] + } else { + td![] }, ] } diff --git a/app/src/pages/list_users.rs b/app/src/pages/list_users.rs index c90de5b..00adfee 100644 --- a/app/src/pages/list_users.rs +++ b/app/src/pages/list_users.rs @@ -241,11 +241,41 @@ pub fn process_user_edit_messages( model.user_edit = None; orders.send_msg(Msg::Query(UserQueryMsg::Fetch)); } - UserEditMsg::MakeAdmin(user) => todo!(), - UserEditMsg::MakeRegular(user) => todo!(), + UserEditMsg::MakeAdmin(user) => update_privileges(user, orders), + UserEditMsg::MakeRegular(user) => update_privileges(user, orders), } } +fn update_privileges(user: UserDelta, orders: &mut impl Orders) { + orders.perform_cmd(async { + let data = user; + // create the request + let request = unwrap_or_return!( + Request::new("/admin/json/update_privileges/") + .method(Method::Post) + .json(&data), + Msg::Edit(UserEditMsg::FailedToCreateUser) + ); + // perform the request and get the response + let response = unwrap_or_return!( + fetch(request).await, + Msg::Edit(UserEditMsg::FailedToCreateUser) + ); + // check for the status + let response = unwrap_or_return!( + response.check_status(), + Msg::Edit(UserEditMsg::FailedToCreateUser) + ); + // deserialize the response + let message: Status = unwrap_or_return!( + response.json().await, + Msg::Edit(UserEditMsg::FailedToCreateUser) + ); + + Msg::Edit(UserEditMsg::UserCreated(message)) + }); +} + fn save_user(user: UserDelta, orders: &mut impl Orders) { orders.perform_cmd(async { let data = user; @@ -281,7 +311,7 @@ fn save_user(user: UserDelta, orders: &mut impl Orders) { #[must_use] /// View the users page. -pub fn view(model: &Model) -> Node { +pub fn view(model: &Model, logged_in_user: &User) -> Node { let lang = model.i18n.clone(); // shortcut for easier translations let t = move |key: &str| lang.translate(key, None); @@ -311,7 +341,10 @@ pub fn view(model: &Model) -> Node { // Add filter fields right below the headlines view_user_table_filter_input(model, &t), // Add all the users one line for each - model.users.iter().map(|u| { view_user(u, &t) }) + model + .users + .iter() + .map(|u| { view_user(u, logged_in_user, &t) }) ], // A refresh button. This will be removed in future versions. button![ @@ -393,36 +426,59 @@ fn view_user_table_filter_input String>(model: &Model, t: F) -> N ] } -fn view_user String>(l: &User, t: F) -> Node { +fn view_user String>(l: &User, logged_in_user: &User, t: F) -> Node { let user = UserDelta::from(l.clone()); tr![ - { - let user = user.clone(); - ev(Ev::Click, |_| { - Msg::Edit(UserEditMsg::EditUserSelected(user)) - }) - }, match l.role { Role::NotAuthenticated | Role::Disabled => C!("inactive"), Role::Regular => C!("regular"), Role::Admin => C!("admin"), }, - td![&l.id], - td![&l.email], - td![&l.username], - match l.role { - Role::NotAuthenticated | Role::Disabled | Role::Regular => td![ - ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected( - user - ))), - t("make-user-admin") - ], - Role::Admin => td![ - ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected( - user - ))), - t("make-user-regular"), - ], + td![ + { + let user = user.clone(); + ev(Ev::Click, |_| { + Msg::Edit(UserEditMsg::EditUserSelected(user)) + }) + }, + &l.id + ], + td![ + { + let user = user.clone(); + ev(Ev::Click, |_| { + Msg::Edit(UserEditMsg::EditUserSelected(user)) + }) + }, + &l.email + ], + td![ + { + let user = user.clone(); + ev(Ev::Click, |_| { + Msg::Edit(UserEditMsg::EditUserSelected(user)) + }) + }, + &l.username + ], + match logged_in_user.role { + Role::Admin => { + match l.role { + Role::NotAuthenticated | Role::Disabled | Role::Regular => td![ + ev(Ev::Click, |_| Msg::Edit(UserEditMsg::MakeAdmin(user))), + t("make-user-admin") + ], + Role::Admin => td![ + ev(Ev::Click, |_| Msg::Edit(UserEditMsg::MakeRegular(user))), + t("make-user-regular"), + ], + } + } + Role::Regular => match l.role { + Role::NotAuthenticated | Role::Disabled | Role::Regular => td![t("user")], + Role::Admin => td![t("admin")], + }, + Role::NotAuthenticated | Role::Disabled => td![], } ] } diff --git a/locales/de/main.ftl b/locales/de/main.ftl index 4da9906..f9ca170 100644 --- a/locales/de/main.ftl +++ b/locales/de/main.ftl @@ -29,6 +29,8 @@ danger-zone-text = Verändern Sie den Code von bereits veröffentlichten Links n save-edits = Speichere die Veränderungen delete-link = Diesen Link löschen +user = Benutzer +admin = Administrator edit-user-headline = Benutzereinstellungen von: {$username} username = Benutzername email = Email diff --git a/locales/en/main.ftl b/locales/en/main.ftl index 872e5d9..a743297 100644 --- a/locales/en/main.ftl +++ b/locales/en/main.ftl @@ -29,6 +29,8 @@ danger-zone-text = Do not change the code of links that are published. If you do save-edits = Save edits delete-link = Delete this link +user = Benutzer +admin = Administrator user-headline = User Settings of: {$username} edit-user-headline = Change Settings of: {$username} username = Username diff --git a/pslink/src/lib.rs b/pslink/src/lib.rs index faa131f..775dfdf 100644 --- a/pslink/src/lib.rs +++ b/pslink/src/lib.rs @@ -207,7 +207,7 @@ pub async fn webservice( ) -> Result { let host_port = format!("{}:{}", &server_config.internal_ip, &server_config.port); info!( - "Running on: {}://{}/admin/login/", + "Running on: {}://{}/apps/", &server_config.protocol, host_port ); info!( @@ -265,6 +265,7 @@ pub async fn webservice( "/update_user/", web::post().to(views::process_update_user_json), ) + .route("/update_privileges/", web::post().to(views::toggle_admin)) .route( "/get_logged_user/", web::post().to(views::get_logged_user_json), diff --git a/pslink/src/queries.rs b/pslink/src/queries.rs index cf76b1e..cb715c7 100644 --- a/pslink/src/queries.rs +++ b/pslink/src/queries.rs @@ -255,6 +255,10 @@ pub async fn list_users( Ok(ListWithOwner { user, list: users }) } + RoleGuard::Regular { user } => Ok(ListWithOwner { + user: user.clone(), + list: vec![user], + }), _ => Err(ServerError::User( "Administrator permissions required".to_owned(), )), @@ -559,14 +563,14 @@ pub async fn update_user( #[instrument(skip(id))] pub async fn toggle_admin( id: &Identity, - user_id: &str, + user_id: Option, server_config: &ServerConfig, ) -> Result, ServerError> { - if let Ok(uid) = user_id.parse::() { + if let Some(uid) = user_id { let auth = authenticate(id, server_config).await?; match auth { RoleGuard::Admin { .. } => { - info!("Changing administrator priviledges: "); + info!("Changing administrator privileges: "); let unchanged_user = User::get_user(uid, server_config).await?; diff --git a/pslink/src/views.rs b/pslink/src/views.rs index c7d472f..f018fbc 100644 --- a/pslink/src/views.rs +++ b/pslink/src/views.rs @@ -207,15 +207,17 @@ pub async fn process_update_user_json( #[instrument(skip(id))] pub async fn toggle_admin( - data: web::Path, + user: web::Json, config: web::Data, id: Identity, ) -> Result { - let update = queries::toggle_admin(&id, &data.0, &config).await?; - Ok(redirect_builder(&format!( - "/admin/view/profile/{}", - update.item.id - ))) + let update = queries::toggle_admin(&id, user.id, &config).await?; + Ok(HttpResponse::Ok().json2(&Status::Success(Message { + message: format!( + "Successfully changed privileges or user: {}", + update.item.username + ), + }))) } #[instrument(skip(id))]