Added admin field to prevent a normal user fetching admin data

This commit is contained in:
xeovalyte 2024-10-09 11:11:05 +02:00
parent e7edea5c85
commit 1aa7a0565d
Signed by: xeovalyte
SSH Key Fingerprint: SHA256:kSQDrQDmKzljJzfGYcd3m9RqHi4h8rSwkZ3sQ9kBURo
10 changed files with 94 additions and 28 deletions

View File

@ -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<Vec<Member>, 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"
}
}
}

View File

@ -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<Member>) -> 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())),

View File

@ -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<Vec<User>, 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)
}

View File

@ -51,3 +51,17 @@ async fn get_user_from_cookie() -> Result<User, ServerFnError> {
Ok(user)
}
#[component]
pub fn AdminLayout() -> Element {
let user_state = use_context::<Signal<Option<User>>>();
let user = user_state().unwrap();
rsx! {
if user.admin {
Outlet::<Route> {}
} else {
div { "No Permissions" }
}
}
}

View File

@ -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::<Signal<Option<User>>>();
let user = user_state().unwrap();
rsx! {
div {
class: "navbar bg-base-200 px-3",
@ -14,11 +18,13 @@ pub fn Topbar() -> Element {
}
div {
class: "flex-none gap-2",
if user.admin {
Link {
class: "btn btn-ghost font-normal",
to: Route::Admin {},
"Administration"
}
}
Link {
class: "btn btn-square btn-ghost",
to: Route::Settings {},

View File

@ -20,4 +20,7 @@ pub enum Error {
#[error("Could not get cookie jar")]
CookieJar(String),
#[error("No permissions")]
NoPermissions,
}

View File

@ -17,11 +17,13 @@ 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("/")]
@ -32,6 +34,8 @@ pub enum Route {
News {},
#[route("/settings")]
Settings {},
#[layout(AdminLayout)]
#[route("/admin")]
Admin {},
#[route("/admin/users")]

View File

@ -64,7 +64,7 @@ impl Session {
pub async fn fetch_user_from_token(session_token: String) -> Result<User, crate::Error> {
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?;

View File

@ -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<String>,
}
#[cfg(feature = "server")]
impl User {
pub async fn fetch_all() -> Result<Vec<Self>, 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<Self, crate::Error> {
// 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<String, crate::Error> {
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?;

View File

@ -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;
",