From 1aa7a0565dd3aee390a53569cd64eb7d4de1335e Mon Sep 17 00:00:00 2001 From: xeovalyte Date: Wed, 9 Oct 2024 11:11:05 +0200 Subject: [PATCH] Added admin field to prevent a normal user fetching admin data --- src/components/admin/members.rs | 18 ++++++++++-- src/components/admin/members/migration.rs | 17 ++++++++++- src/components/admin/users.rs | 9 +++++- src/components/layout.rs | 14 +++++++++ src/components/layout/topbar.rs | 14 ++++++--- src/err.rs | 3 ++ src/main.rs | 36 +++++++++++++---------- src/util/model/session.rs | 2 +- src/util/model/user.rs | 7 +++-- src/util/surrealdb.rs | 2 ++ 10 files changed, 94 insertions(+), 28 deletions(-) diff --git a/src/components/admin/members.rs b/src/components/admin/members.rs index db23a08..e61ca4b 100644 --- a/src/components/admin/members.rs +++ b/src/components/admin/members.rs @@ -2,7 +2,7 @@ pub mod migration; use dioxus::prelude::*; -use crate::util::model::member::Member; +use crate::util::model::{member::Member, session::Session}; pub fn Members() -> Element { let members = use_resource(fetch_members); @@ -58,13 +58,27 @@ fn MemberRow(props: MemberRowProps) -> Element { class: "hover hover:cursor-pointer", th { "{props.member.id}" } td { "{props.member.name.full}" } - td { "{registration_token} {props.member.hours:?}" } + td { "{registration_token}" } } } } #[server] async fn fetch_members() -> Result, ServerFnError> { + let user = Session::fetch_current_user().await?; + + if !user.admin { + return Err(crate::Error::NoPermissions.into()); + } + let members = Member::fetch_all().await?; Ok(members) } + +fn MemberModal(props: MemberRowProps) -> Element { + rsx! { + div { + "BOe" + } + } +} diff --git a/src/components/admin/members/migration.rs b/src/components/admin/members/migration.rs index 3aea920..8dc2ad1 100644 --- a/src/components/admin/members/migration.rs +++ b/src/components/admin/members/migration.rs @@ -1,4 +1,7 @@ -use crate::util::model::member::{Member, MembersMigration}; +use crate::util::model::{ + member::{Member, MembersMigration}, + session::Session, +}; use dioxus::prelude::{dioxus_elements::FileEngine, *}; use std::sync::Arc; @@ -209,6 +212,12 @@ fn MembersTable(members: Vec) -> Element { #[server] async fn upload_members_list(input: String) -> Result<(u16, MembersMigration), ServerFnError> { + let user = Session::fetch_current_user().await?; + + if !user.admin { + return Err(crate::Error::NoPermissions.into()); + } + match MembersMigration::migrate_proposal(input).await { Ok(r) => Ok(r), Err(err) => Err(ServerFnError::new(err.to_string())), @@ -217,6 +226,12 @@ async fn upload_members_list(input: String) -> Result<(u16, MembersMigration), S #[server] async fn migration_response(correct: bool, id: u16) -> Result<(), ServerFnError> { + let user = Session::fetch_current_user().await?; + + if !user.admin { + return Err(crate::Error::NoPermissions.into()); + } + if correct { match MembersMigration::migrate(id).await { Err(err) => Err(ServerFnError::new(err.to_string())), diff --git a/src/components/admin/users.rs b/src/components/admin/users.rs index e50fefa..f750c5e 100644 --- a/src/components/admin/users.rs +++ b/src/components/admin/users.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; -use crate::util::model::user::User; +use crate::util::model::{session::Session, user::User}; pub fn Users() -> Element { let users = use_resource(fetch_users); @@ -56,6 +56,13 @@ fn UserRow(props: UserRowProps) -> Element { #[server] async fn fetch_users() -> Result, ServerFnError> { + let user = Session::fetch_current_user().await?; + + if !user.admin { + return Err(crate::Error::NoPermissions.into()); + } + let users = User::fetch_all().await?; + Ok(users) } diff --git a/src/components/layout.rs b/src/components/layout.rs index dc98666..7e460d7 100644 --- a/src/components/layout.rs +++ b/src/components/layout.rs @@ -51,3 +51,17 @@ async fn get_user_from_cookie() -> Result { Ok(user) } + +#[component] +pub fn AdminLayout() -> Element { + let user_state = use_context::>>(); + let user = user_state().unwrap(); + + rsx! { + if user.admin { + Outlet:: {} + } else { + div { "No Permissions" } + } + } +} diff --git a/src/components/layout/topbar.rs b/src/components/layout/topbar.rs index 212d68e..db316e7 100644 --- a/src/components/layout/topbar.rs +++ b/src/components/layout/topbar.rs @@ -2,9 +2,13 @@ use crate::Route; use dioxus::prelude::*; use super::icons; +use crate::util::model::user::User; #[component] pub fn Topbar() -> Element { + let user_state = use_context::>>(); + let user = user_state().unwrap(); + rsx! { div { class: "navbar bg-base-200 px-3", @@ -14,10 +18,12 @@ pub fn Topbar() -> Element { } div { class: "flex-none gap-2", - Link { - class: "btn btn-ghost font-normal", - to: Route::Admin {}, - "Administration" + if user.admin { + Link { + class: "btn btn-ghost font-normal", + to: Route::Admin {}, + "Administration" + } } Link { class: "btn btn-square btn-ghost", diff --git a/src/err.rs b/src/err.rs index b3158ca..ac11624 100644 --- a/src/err.rs +++ b/src/err.rs @@ -20,4 +20,7 @@ pub enum Error { #[error("Could not get cookie jar")] CookieJar(String), + + #[error("No permissions")] + NoPermissions, } diff --git a/src/main.rs b/src/main.rs index c5bcdd2..e977303 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,29 +17,33 @@ use components::admin::users::Users; use components::admin::Admin; use components::agenda::Agenda; use components::home::Home; +use components::layout::AdminLayout; use components::layout::Global; use components::news::News; use components::settings::Settings; #[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[rustfmt::skip] pub enum Route { #[layout(Global)] - #[route("/")] - Home {}, - #[route("/agenda")] - Agenda {}, - #[route("/news")] - News {}, - #[route("/settings")] - Settings {}, - #[route("/admin")] - Admin {}, - #[route("/admin/users")] - Users {}, - #[route("/admin/members")] - Members {}, - #[route("/admin/members/migration")] - Migration {}, + #[route("/")] + Home {}, + #[route("/agenda")] + Agenda {}, + #[route("/news")] + News {}, + #[route("/settings")] + Settings {}, + + #[layout(AdminLayout)] + #[route("/admin")] + Admin {}, + #[route("/admin/users")] + Users {}, + #[route("/admin/members")] + Members {}, + #[route("/admin/members/migration")] + Migration {}, } const _TAILWIND_URL: &str = manganis::mg!(file("assets/tailwind.css")); diff --git a/src/util/model/session.rs b/src/util/model/session.rs index 385245e..613a9b0 100644 --- a/src/util/model/session.rs +++ b/src/util/model/session.rs @@ -64,7 +64,7 @@ impl Session { pub async fn fetch_user_from_token(session_token: String) -> Result { let mut res = DB .query( - "SELECT user as id, user.email as email FROM session WHERE token = $session_token", + "SELECT user as id, user.email as email, user.admin as admin FROM session WHERE token = $session_token", ) .bind(("session_token", session_token)) .await?; diff --git a/src/util/model/user.rs b/src/util/model/user.rs index 17b46f5..dc55c64 100644 --- a/src/util/model/user.rs +++ b/src/util/model/user.rs @@ -13,13 +13,14 @@ pub struct User { #[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))] pub id: String, pub email: String, + pub admin: bool, password: Option, } #[cfg(feature = "server")] impl User { pub async fn fetch_all() -> Result, surrealdb::Error> { - let mut res = DB.query("SELECT id, email FROM user").await?; + let mut res = DB.query("SELECT id, email, admin FROM user").await?; res = res.check()?; @@ -35,7 +36,7 @@ impl User { ) -> Result { // Create user record let mut transaction = DB.query(BeginStatement::default()) - .query("let $user = CREATE ONLY user SET email = $email, password = crypto::argon2::generate($password) RETURN id, email;") + .query("let $user = CREATE ONLY user SET email = $email, password = crypto::argon2::generate($password) RETURN id, email, admin;") .bind(("email", email)) .bind(("password", password)); @@ -103,7 +104,7 @@ impl User { password: String, ) -> Result { let mut res = DB - .query("SELECT id, email FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)") + .query("SELECT id, email, admin FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)") .bind(("email", email)) .bind(("password", password)) .await?; diff --git a/src/util/surrealdb.rs b/src/util/surrealdb.rs index d6b3115..59a8403 100644 --- a/src/util/surrealdb.rs +++ b/src/util/surrealdb.rs @@ -41,6 +41,8 @@ async fn apply_queries() -> surrealdb::Result<()> { VALUE string::lowercase($value) ASSERT string::is::email($value); DEFINE FIELD password ON TABLE user TYPE string; + DEFINE FIELD admin ON TABLE user TYPE bool + DEFAULT false; DEFINE INDEX userEmailIndex ON TABLE user COLUMNS email UNIQUE; ",