admin pages

Pages can now be locked behind admin privileges
This commit is contained in:
2026-05-01 16:47:42 +02:00
parent 802e5876f7
commit fdd2e2a757
5 changed files with 94 additions and 36 deletions

View File

@@ -163,7 +163,8 @@ pub async fn get_current_user(
"data": json!({
"id": user.id,
"first_name": user.first_name,
"last_name": user.last_name
"last_name": user.last_name,
"is_admin": user.is_admin
})
});

View File

@@ -5,22 +5,27 @@ use yew_router::prelude::*;
#[derive(Clone, Debug, PartialEq)]
pub struct AuthState {
pub is_authenticated: bool,
pub is_authenticated: Option<bool>,
pub is_admin: Option<bool>,
}
#[derive(Properties, PartialEq)]
pub struct ProtectedRouteProps {
pub children: Children,
pub admin_page: bool,
}
#[component(ProtectedRoute)]
pub fn protected_route(props: &ProtectedRouteProps) -> Html {
let is_authenticated = use_state(|| None::<bool>);
let auth_state = use_state(|| AuthState {
is_authenticated: None,
is_admin: None,
});
{
let is_authenticated = is_authenticated.clone();
let auth_state = auth_state.clone();
use_effect_with((), move |_| {
let is_authenticated = is_authenticated.clone();
let auth_state = auth_state.clone();
spawn_local(async move {
match Request::get("/api/users/current")
.credentials(web_sys::RequestCredentials::Include)
@@ -31,14 +36,27 @@ pub fn protected_route(props: &ProtectedRouteProps) -> Html {
let status = resp.status();
web_sys::console::log_1(&format!("Auth check: status {}", status).into());
if status == 200 {
is_authenticated.set(Some(true));
let user_data: serde_json::Value =
resp.json().await.unwrap_or_default();
let is_admin = user_data["data"]["is_admin"].as_bool();
auth_state.set(AuthState {
is_authenticated: Some(true),
is_admin,
});
} else {
is_authenticated.set(Some(false));
auth_state.set(AuthState {
is_authenticated: Some(false),
is_admin: Some(false),
});
}
}
Err(err) => {
web_sys::console::log_1(&format!("Auth check error: {:?}", err).into());
is_authenticated.set(Some(false));
auth_state.set(AuthState {
is_authenticated: Some(false),
is_admin: Some(false),
});
}
}
});
@@ -46,13 +64,33 @@ pub fn protected_route(props: &ProtectedRouteProps) -> Html {
});
}
match *is_authenticated {
None => html! { <div>{"Loading..."}</div> },
Some(true) => props.children.clone().into(),
Some(false) => {
html! {
<Redirect<crate::Route> to={crate::Route::Login} />
match (*auth_state) {
AuthState {
is_authenticated: None,
..
} => html! { <div>{ "Loading..." } </div> },
AuthState {
is_authenticated: Some(false),
..
} => html! {
<Redirect<crate::Route> to={crate::Route::Login}/>
},
AuthState {
is_authenticated: Some(true),
is_admin: admin_flag,
} => {
if props.admin_page {
match admin_flag {
Some(true) => props.children.clone().into(),
Some(false) => {
html! { <Redirect<crate::Route> to={crate::Route::PermissionDenied}/> }
}
None => html! { <div>{ "Checking permissions..." }</div> },
}
} else {
props.children.clone().into()
}
}
_ => html! { <div>{ "Checking permissions..." }</div> },
}
}

View File

@@ -1,7 +1,7 @@
mod pages;
mod auth;
use crate::pages::*;
mod pages;
use crate::auth::ProtectedRoute;
use crate::pages::*;
use yew::prelude::*;
use yew_router::prelude::*;
@@ -23,6 +23,8 @@ enum Route {
AllUsers,
#[at("/users/:id")]
UserByID { id: i16 },
#[at("/denied")]
PermissionDenied,
#[not_found]
#[at("/404")]
NotFound,
@@ -31,42 +33,43 @@ enum Route {
fn switch(route: Route) -> Html {
match route {
Route::Home => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={false}>
<basic_pages::Home/>
</ProtectedRoute>
},
Route::NotFound => html! { <basic_pages::NotFound/> },
Route::Ticket => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={false}>
<ticket::SubmitTicket/>
</ProtectedRoute>
},
Route::TicketById { id } => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={true}>
<ticket::TicketByID {id}/>
</ProtectedRoute>
},
Route::AllTickets => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={false}>
<ticket::AllTickets/>
</ProtectedRoute>
},
Route::Register => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={true}>
<user::Register/>
</ProtectedRoute>
},
Route::Login => html! { <user::Login/> },
Route::AllUsers => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={true}>
<user::AllUsers/>
</ProtectedRoute>
},
Route::UserByID { id } => html! {
<ProtectedRoute>
<ProtectedRoute admin_page={true}>
<user::UserByID {id}/>
</ProtectedRoute>
},
Route::PermissionDenied => html! { <basic_pages::PermissionDenied/> },
}
}

View File

@@ -28,3 +28,13 @@ pub fn not_found_component() -> Html {
</div>
}
}
#[component(PermissionDenied)]
pub fn denied_component() -> Html {
html! {
<div>
<h1>{ "Sie haben nicht die benötigten Rechte um diese Seite aufzurufen" }</h1>
<h3>{ "Wenn sie denken, dass dies ein Fehler ist kontaktieren sie Herrn Winter" }</h3>
</div>
}
}

View File

@@ -4,6 +4,7 @@ use gloo_net::http::Request;
use serde::{Deserialize, Serialize};
use wasm_bindgen_futures::spawn_local;
use yew::prelude::*;
use yew_router::prelude::use_navigator;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct User {
@@ -177,6 +178,7 @@ pub fn login_component() -> Html {
let loading = use_state(|| false);
let error = use_state(|| String::new());
let success = use_state(|| false);
let navigator = use_navigator().unwrap();
let onsubmit = {
let username = username.clone();
@@ -184,6 +186,7 @@ pub fn login_component() -> Html {
let loading = loading.clone();
let error = error.clone();
let success = success.clone();
let navigator = navigator.clone();
Callback::from(move |e: SubmitEvent| {
e.prevent_default();
@@ -192,6 +195,7 @@ pub fn login_component() -> Html {
let loading = loading.clone();
let error = error.clone();
let success = success.clone();
let navigator = navigator.clone();
loading.set(true);
error.set(String::new());
@@ -211,7 +215,10 @@ pub fn login_component() -> Html {
loading.set(false);
match response {
Ok(r) if r.status() == 200 => success.set(true),
Ok(r) if r.status() == 200 => {
success.set(true);
navigator.push(&crate::Route::Home);
}
Ok(r) => {
let text = r.text().await.unwrap_or_else(|_| "unknown".into());
error.set(format!("HTTP {}: {}", r.status(), text));
@@ -242,7 +249,6 @@ pub fn login_component() -> Html {
})}
/>
<button type="submit" disabled={*loading}>{ if *loading { "Logging in..." } else { "Login" } }</button>
if *success { <p>{"Logged in — cookie set by server"}</p> }
if !error.is_empty() { <p style="color:red">{(*error).clone()}</p> }
</form>
}