make privileges more defining for UI
This commit is contained in:
parent
eee0a8dba2
commit
f361a13c91
@ -335,7 +335,7 @@ fn view(model: &Model) -> Node<Msg> {
|
|||||||
match model.user {
|
match model.user {
|
||||||
Loadable::Data(Some(ref user)) => div![
|
Loadable::Data(Some(ref user)) => div![
|
||||||
navigation::navigation(&model.i18n, &model.location.base_url, user),
|
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::Data(None) => view_login(&model.i18n, model),
|
||||||
Loadable::Loading => div![C!("lds-ellipsis"), div!(), div!(), div!(), div!()],
|
Loadable::Loading => div![C!("lds-ellipsis"), div!(), div!(), div!(), div!()],
|
||||||
@ -344,12 +344,12 @@ fn view(model: &Model) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Render the subpages.
|
/// Render the subpages.
|
||||||
fn view_content(page: &Page, url: &Url) -> Node<Msg> {
|
fn view_content(page: &Page, url: &Url, user: &User) -> Node<Msg> {
|
||||||
div![
|
div![
|
||||||
C!["container"],
|
C!["container"],
|
||||||
match page {
|
match page {
|
||||||
Page::Home(model) => pages::list_links::view(model).map_msg(Msg::ListLinks),
|
Page::Home(model) => pages::list_links::view(model, user).map_msg(Msg::ListLinks),
|
||||||
Page::ListUsers(model) => pages::list_users::view(model).map_msg(Msg::ListUsers),
|
Page::ListUsers(model) => pages::list_users::view(model, user).map_msg(Msg::ListUsers),
|
||||||
Page::NotFound => div![div![url.to_string()], "Page not found!"],
|
Page::NotFound => div![div![url.to_string()], "Page not found!"],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -47,16 +47,16 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node<Msg> {
|
|||||||
)
|
)
|
||||||
)),
|
)),
|
||||||
t("invite-user"),
|
t("invite-user"),
|
||||||
],],
|
],]
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
nodes!()
|
||||||
|
},
|
||||||
// A button to list all users
|
// A button to list all users
|
||||||
li![a![
|
li![a![
|
||||||
attrs! {At::Href => crate::Urls::new(base_url).list_users()},
|
attrs! {At::Href => crate::Urls::new(base_url).list_users()},
|
||||||
t("list-users"),
|
t("list-users"),
|
||||||
],],
|
],],
|
||||||
]
|
|
||||||
} else {
|
|
||||||
nodes!()
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
ol![
|
ol![
|
||||||
li![div![
|
li![div![
|
||||||
|
@ -13,7 +13,7 @@ use shared::{
|
|||||||
general::{EditMode, Message, Operation, Status},
|
general::{EditMode, Message, Operation, Status},
|
||||||
links::{LinkDelta, LinkOverviewColumns, LinkRequestForm},
|
links::{LinkDelta, LinkOverviewColumns, LinkRequestForm},
|
||||||
},
|
},
|
||||||
datatypes::{FullLink, Lang, Loadable},
|
datatypes::{FullLink, Lang, Loadable, User},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{get_host, i18n::I18n, unwrap_or_return};
|
use crate::{get_host, i18n::I18n, unwrap_or_return};
|
||||||
@ -456,7 +456,7 @@ fn delete_link(link_delta: LinkDelta, orders: &mut impl Orders<Msg>) {
|
|||||||
/// * questions
|
/// * questions
|
||||||
/// * the table of links including sorting and searching
|
/// * the table of links including sorting and searching
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model, logged_in_user: &User) -> Node<Msg> {
|
||||||
let lang = &model.i18n.clone();
|
let lang = &model.i18n.clone();
|
||||||
// shortcut for translating
|
// shortcut for translating
|
||||||
let t = move |key: &str| lang.translate(key, None);
|
let t = move |key: &str| lang.translate(key, None);
|
||||||
@ -498,7 +498,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
// Add filter fields right below the headlines
|
// Add filter fields right below the headlines
|
||||||
view_link_table_filter_input(model, &t),
|
view_link_table_filter_input(model, &t),
|
||||||
// Add all the content lines
|
// 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.
|
// A fetch button - this should not be needed and will be removed in future.
|
||||||
button![
|
button![
|
||||||
@ -541,6 +541,7 @@ fn view_link_table_head<F: Fn(&str) -> String>(t: F) -> Node<Msg> {
|
|||||||
))),
|
))),
|
||||||
t("statistics")
|
t("statistics")
|
||||||
],
|
],
|
||||||
|
th![],
|
||||||
th![]
|
th![]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -593,43 +594,34 @@ fn view_link_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> N
|
|||||||
// statistics and the delete column cannot be filtered
|
// statistics and the delete column cannot be filtered
|
||||||
td![],
|
td![],
|
||||||
td![],
|
td![],
|
||||||
|
td![],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// display a single table row containing one link
|
/// display a single table row containing one link
|
||||||
fn view_link(l: &FullLink) -> Node<Msg> {
|
fn view_link(l: &FullLink, logged_in_user: &User) -> Node<Msg> {
|
||||||
// Ugly hack - this is needed to be able to move the l into the closures... l.clone() in place does not work.
|
use shared::apirequests::users::Role;
|
||||||
let link = LinkDelta::from(l.clone());
|
macro_rules! event_or_not {
|
||||||
let link2 = LinkDelta::from(l.clone());
|
( $link:expr, $user:expr, $content:expr) => {
|
||||||
let link3 = LinkDelta::from(l.clone());
|
if $user.role == Role::Admin
|
||||||
let link4 = LinkDelta::from(l.clone());
|
|| ($user.role == Role::Regular) && $link.user.id == $user.id
|
||||||
let link5 = LinkDelta::from(l.clone());
|
|
||||||
tr![
|
|
||||||
{
|
{
|
||||||
|
let link = LinkDelta::from($link.clone());
|
||||||
td![
|
td![
|
||||||
ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link))),
|
ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link))),
|
||||||
&l.link.code
|
$content
|
||||||
]
|
]
|
||||||
},
|
} else {
|
||||||
{
|
td![$content]
|
||||||
td![
|
}
|
||||||
ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link2))),
|
};
|
||||||
&l.link.title
|
}
|
||||||
]
|
tr![
|
||||||
},
|
event_or_not!(l, logged_in_user, &l.link.code),
|
||||||
td![a![attrs![At::Href => &l.link.target], &l.link.target]],
|
event_or_not!(l, logged_in_user, &l.link.title),
|
||||||
{
|
event_or_not!(l, logged_in_user, &l.link.target),
|
||||||
td![
|
event_or_not!(l, logged_in_user, &l.user.username),
|
||||||
ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link3))),
|
event_or_not!(l, logged_in_user, &l.clicks.number),
|
||||||
&l.user.username
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
td![
|
|
||||||
ev(Ev::Click, |_| Msg::Edit(EditMsg::EditSelected(link4))),
|
|
||||||
&l.clicks.number
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
td![
|
td![
|
||||||
C!["table_qr"],
|
C!["table_qr"],
|
||||||
@ -639,12 +631,16 @@ fn view_link(l: &FullLink) -> Node<Msg> {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
if logged_in_user.role == Role::Admin
|
||||||
|
|| (logged_in_user.role == Role::Regular) && l.user.id == logged_in_user.id
|
||||||
{
|
{
|
||||||
td![img![
|
let link = LinkDelta::from(l.clone());
|
||||||
ev(Ev::Click, |_| Msg::Edit(EditMsg::MayDeleteSelected(link5))),
|
td![
|
||||||
C!["trashicon"],
|
ev(Ev::Click, |_| Msg::Edit(EditMsg::MayDeleteSelected(link))),
|
||||||
attrs!(At::Src => "/static/trash.svg")
|
img![C!["trashicon"], attrs!(At::Src => "/static/trash.svg")]
|
||||||
]]
|
]
|
||||||
|
} else {
|
||||||
|
td![]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -241,11 +241,41 @@ pub fn process_user_edit_messages(
|
|||||||
model.user_edit = None;
|
model.user_edit = None;
|
||||||
orders.send_msg(Msg::Query(UserQueryMsg::Fetch));
|
orders.send_msg(Msg::Query(UserQueryMsg::Fetch));
|
||||||
}
|
}
|
||||||
UserEditMsg::MakeAdmin(user) => todo!(),
|
UserEditMsg::MakeAdmin(user) => update_privileges(user, orders),
|
||||||
UserEditMsg::MakeRegular(user) => todo!(),
|
UserEditMsg::MakeRegular(user) => update_privileges(user, orders),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_privileges(user: UserDelta, orders: &mut impl Orders<Msg>) {
|
||||||
|
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<Msg>) {
|
fn save_user(user: UserDelta, orders: &mut impl Orders<Msg>) {
|
||||||
orders.perform_cmd(async {
|
orders.perform_cmd(async {
|
||||||
let data = user;
|
let data = user;
|
||||||
@ -281,7 +311,7 @@ fn save_user(user: UserDelta, orders: &mut impl Orders<Msg>) {
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// View the users page.
|
/// View the users page.
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model, logged_in_user: &User) -> Node<Msg> {
|
||||||
let lang = model.i18n.clone();
|
let lang = model.i18n.clone();
|
||||||
// shortcut for easier translations
|
// shortcut for easier translations
|
||||||
let t = move |key: &str| lang.translate(key, None);
|
let t = move |key: &str| lang.translate(key, None);
|
||||||
@ -311,7 +341,10 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
// Add filter fields right below the headlines
|
// Add filter fields right below the headlines
|
||||||
view_user_table_filter_input(model, &t),
|
view_user_table_filter_input(model, &t),
|
||||||
// Add all the users one line for each
|
// 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.
|
// A refresh button. This will be removed in future versions.
|
||||||
button![
|
button![
|
||||||
@ -393,37 +426,60 @@ fn view_user_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> N
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view_user<F: Fn(&str) -> String>(l: &User, t: F) -> Node<Msg> {
|
fn view_user<F: Fn(&str) -> String>(l: &User, logged_in_user: &User, t: F) -> Node<Msg> {
|
||||||
let user = UserDelta::from(l.clone());
|
let user = UserDelta::from(l.clone());
|
||||||
tr![
|
tr![
|
||||||
|
match l.role {
|
||||||
|
Role::NotAuthenticated | Role::Disabled => C!("inactive"),
|
||||||
|
Role::Regular => C!("regular"),
|
||||||
|
Role::Admin => C!("admin"),
|
||||||
|
},
|
||||||
|
td![
|
||||||
{
|
{
|
||||||
let user = user.clone();
|
let user = user.clone();
|
||||||
ev(Ev::Click, |_| {
|
ev(Ev::Click, |_| {
|
||||||
Msg::Edit(UserEditMsg::EditUserSelected(user))
|
Msg::Edit(UserEditMsg::EditUserSelected(user))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
match l.role {
|
&l.id
|
||||||
Role::NotAuthenticated | Role::Disabled => C!("inactive"),
|
],
|
||||||
Role::Regular => C!("regular"),
|
td![
|
||||||
Role::Admin => C!("admin"),
|
{
|
||||||
|
let user = user.clone();
|
||||||
|
ev(Ev::Click, |_| {
|
||||||
|
Msg::Edit(UserEditMsg::EditUserSelected(user))
|
||||||
|
})
|
||||||
},
|
},
|
||||||
td![&l.id],
|
&l.email
|
||||||
td![&l.email],
|
],
|
||||||
td![&l.username],
|
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 {
|
match l.role {
|
||||||
Role::NotAuthenticated | Role::Disabled | Role::Regular => td![
|
Role::NotAuthenticated | Role::Disabled | Role::Regular => td![
|
||||||
ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected(
|
ev(Ev::Click, |_| Msg::Edit(UserEditMsg::MakeAdmin(user))),
|
||||||
user
|
|
||||||
))),
|
|
||||||
t("make-user-admin")
|
t("make-user-admin")
|
||||||
],
|
],
|
||||||
Role::Admin => td![
|
Role::Admin => td![
|
||||||
ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected(
|
ev(Ev::Click, |_| Msg::Edit(UserEditMsg::MakeRegular(user))),
|
||||||
user
|
|
||||||
))),
|
|
||||||
t("make-user-regular"),
|
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![],
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ danger-zone-text = Verändern Sie den Code von bereits veröffentlichten Links n
|
|||||||
save-edits = Speichere die Veränderungen
|
save-edits = Speichere die Veränderungen
|
||||||
delete-link = Diesen Link löschen
|
delete-link = Diesen Link löschen
|
||||||
|
|
||||||
|
user = Benutzer
|
||||||
|
admin = Administrator
|
||||||
edit-user-headline = Benutzereinstellungen von: {$username}
|
edit-user-headline = Benutzereinstellungen von: {$username}
|
||||||
username = Benutzername
|
username = Benutzername
|
||||||
email = Email
|
email = Email
|
||||||
|
@ -29,6 +29,8 @@ danger-zone-text = Do not change the code of links that are published. If you do
|
|||||||
save-edits = Save edits
|
save-edits = Save edits
|
||||||
delete-link = Delete this link
|
delete-link = Delete this link
|
||||||
|
|
||||||
|
user = Benutzer
|
||||||
|
admin = Administrator
|
||||||
user-headline = User Settings of: {$username}
|
user-headline = User Settings of: {$username}
|
||||||
edit-user-headline = Change Settings of: {$username}
|
edit-user-headline = Change Settings of: {$username}
|
||||||
username = Username
|
username = Username
|
||||||
|
@ -207,7 +207,7 @@ pub async fn webservice(
|
|||||||
) -> Result<actix_web::dev::Server, std::io::Error> {
|
) -> Result<actix_web::dev::Server, std::io::Error> {
|
||||||
let host_port = format!("{}:{}", &server_config.internal_ip, &server_config.port);
|
let host_port = format!("{}:{}", &server_config.internal_ip, &server_config.port);
|
||||||
info!(
|
info!(
|
||||||
"Running on: {}://{}/admin/login/",
|
"Running on: {}://{}/apps/",
|
||||||
&server_config.protocol, host_port
|
&server_config.protocol, host_port
|
||||||
);
|
);
|
||||||
info!(
|
info!(
|
||||||
@ -265,6 +265,7 @@ pub async fn webservice(
|
|||||||
"/update_user/",
|
"/update_user/",
|
||||||
web::post().to(views::process_update_user_json),
|
web::post().to(views::process_update_user_json),
|
||||||
)
|
)
|
||||||
|
.route("/update_privileges/", web::post().to(views::toggle_admin))
|
||||||
.route(
|
.route(
|
||||||
"/get_logged_user/",
|
"/get_logged_user/",
|
||||||
web::post().to(views::get_logged_user_json),
|
web::post().to(views::get_logged_user_json),
|
||||||
|
@ -255,6 +255,10 @@ pub async fn list_users(
|
|||||||
|
|
||||||
Ok(ListWithOwner { user, list: users })
|
Ok(ListWithOwner { user, list: users })
|
||||||
}
|
}
|
||||||
|
RoleGuard::Regular { user } => Ok(ListWithOwner {
|
||||||
|
user: user.clone(),
|
||||||
|
list: vec![user],
|
||||||
|
}),
|
||||||
_ => Err(ServerError::User(
|
_ => Err(ServerError::User(
|
||||||
"Administrator permissions required".to_owned(),
|
"Administrator permissions required".to_owned(),
|
||||||
)),
|
)),
|
||||||
@ -559,14 +563,14 @@ pub async fn update_user(
|
|||||||
#[instrument(skip(id))]
|
#[instrument(skip(id))]
|
||||||
pub async fn toggle_admin(
|
pub async fn toggle_admin(
|
||||||
id: &Identity,
|
id: &Identity,
|
||||||
user_id: &str,
|
user_id: Option<i64>,
|
||||||
server_config: &ServerConfig,
|
server_config: &ServerConfig,
|
||||||
) -> Result<Item<User>, ServerError> {
|
) -> Result<Item<User>, ServerError> {
|
||||||
if let Ok(uid) = user_id.parse::<i64>() {
|
if let Some(uid) = user_id {
|
||||||
let auth = authenticate(id, server_config).await?;
|
let auth = authenticate(id, server_config).await?;
|
||||||
match auth {
|
match auth {
|
||||||
RoleGuard::Admin { .. } => {
|
RoleGuard::Admin { .. } => {
|
||||||
info!("Changing administrator priviledges: ");
|
info!("Changing administrator privileges: ");
|
||||||
|
|
||||||
let unchanged_user = User::get_user(uid, server_config).await?;
|
let unchanged_user = User::get_user(uid, server_config).await?;
|
||||||
|
|
||||||
|
@ -207,15 +207,17 @@ pub async fn process_update_user_json(
|
|||||||
|
|
||||||
#[instrument(skip(id))]
|
#[instrument(skip(id))]
|
||||||
pub async fn toggle_admin(
|
pub async fn toggle_admin(
|
||||||
data: web::Path<String>,
|
user: web::Json<UserDelta>,
|
||||||
config: web::Data<crate::ServerConfig>,
|
config: web::Data<crate::ServerConfig>,
|
||||||
id: Identity,
|
id: Identity,
|
||||||
) -> Result<HttpResponse, ServerError> {
|
) -> Result<HttpResponse, ServerError> {
|
||||||
let update = queries::toggle_admin(&id, &data.0, &config).await?;
|
let update = queries::toggle_admin(&id, user.id, &config).await?;
|
||||||
Ok(redirect_builder(&format!(
|
Ok(HttpResponse::Ok().json2(&Status::Success(Message {
|
||||||
"/admin/view/profile/{}",
|
message: format!(
|
||||||
update.item.id
|
"Successfully changed privileges or user: {}",
|
||||||
)))
|
update.item.username
|
||||||
|
),
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(id))]
|
#[instrument(skip(id))]
|
||||||
|
Loading…
Reference in New Issue
Block a user