From 4f152df0061128956c7b1a367179bfcd6ff9a7fe Mon Sep 17 00:00:00 2001 From: Chris Jean-Marie Date: Mon, 30 Sep 2024 00:15:25 +0000 Subject: [PATCH] Fix loading of profiles from user administration screen --- backend/src/main.rs | 11 +-- backend/src/middlewares.rs | 76 +++++++++++++++++---- backend/src/routes.rs | 51 ++++++++++---- backend/templates/base.html | 74 ++++++++++----------- backend/templates/index.html | 111 ++++++++++++++++++------------- backend/templates/profile.html | 8 +-- backend/templates/useradmin.html | 24 +++++-- 7 files changed, 226 insertions(+), 129 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 663041e..fcff6bc 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -51,8 +51,7 @@ pub struct AppState { pub db_pool: SqlitePool, } -#[derive(Clone, Debug, FromRow)] -#[derive(Serialize, Deserialize)] +#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)] pub struct UserData { #[allow(dead_code)] pub id: i64, @@ -60,12 +59,7 @@ pub struct UserData { pub name: String, pub family_name: String, pub given_name: String, -/* pub discriminator: String, - pub avatar: String, - pub access_token: String, - pub refresh_token: String, - pub expires_at: i64, - */} +} #[tokio::main] async fn main() { @@ -93,6 +87,7 @@ async fn main() { //Routes that require authentication .route("/profile", get(profile)) .route("/useradmin", get(useradmin)) + .route("/users/:user_id", get(user_profile)) .route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), check_auth)) //Routes that don't require authentication diff --git a/backend/src/middlewares.rs b/backend/src/middlewares.rs index 13ec81f..9a0a533 100644 --- a/backend/src/middlewares.rs +++ b/backend/src/middlewares.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use super::{AppError, UserData}; use axum::{ body::Body, @@ -43,9 +45,13 @@ pub async fn inject_user_data( let id = query.0; let expires_at = query.1; if expires_at > Utc::now().timestamp() { - let row = sqlx::query_as!(UserData, "SELECT * FROM users WHERE id = ?", id) - .fetch_one(&db_pool) - .await?; + let row = sqlx::query_as!( + UserData, + "SELECT * FROM users WHERE id = ?", + id + ) + .fetch_one(&db_pool) + .await?; request.extensions_mut().insert(Some(UserData { id: row.id, @@ -65,8 +71,11 @@ pub async fn inject_user_data( Ok(next.run(request).await) } -pub async fn check_auth(State(app_state): State, request: Request, next: Next) -> Result { - +pub async fn check_auth( + State(app_state): State, + request: Request, + next: Next, +) -> Result { if request .extensions() .get::>() @@ -75,19 +84,60 @@ pub async fn check_auth(State(app_state): State, request: Request>().unwrap().as_ref().unwrap().email); + println!( + "{}", + &*request + .extensions() + .get::>() + .unwrap() + .as_ref() + .unwrap() + .email + ); let query: Result<(i64,), _> = match path { - "/profile" => + "/profile" => { sqlx::query_as(r#"select u.id from users u where email =?"#) + .bind( + request + .extensions() + .get::>() + .unwrap() + .as_ref() + .unwrap() + .email + .as_str(), + ) + .fetch_one(&app_state) + .await + } + _ => { + // loop through path to find a permission + let mut remaining_path = Path::new(path); + loop { + let query: Result<(String,), _> = sqlx::query_as(r#"select r.item from role_permissions r join user_roles ur on ur.role_id = r.role_id join users u on u.id = ur.user_id where item = ? and email =?"#) + .bind(remaining_path.to_str().unwrap()) + .bind(request.extensions().get::>().unwrap().as_ref().unwrap().email.as_str()) + .fetch_one(&app_state) + .await; + if let Ok(query) = query { + if query.0 != "" { + break; + } + } + if remaining_path.parent().is_none() { + break; + } + remaining_path = remaining_path.parent().unwrap(); + + println!("{}", remaining_path.to_str().unwrap()); + } + sqlx::query_as(r#"select u.id from role_permissions r join user_roles ur on ur.role_id = r.role_id join users u on u.id = ur.user_id where item = ? and email =?"#) + .bind(remaining_path.to_str().unwrap()) .bind(request.extensions().get::>().unwrap().as_ref().unwrap().email.as_str()) .fetch_one(&app_state) - .await, - _ => sqlx::query_as(r#"select u.id from role_permissions r join user_roles ur on ur.role_id = r.role_id join users u on u.id = ur.user_id where item = ? and email =?"#) - .bind(&*request.uri().to_string()) - .bind(request.extensions().get::>().unwrap().as_ref().unwrap().email.as_str()) - .fetch_one(&app_state) - .await, + .await + } }; // user is logged in diff --git a/backend/src/routes.rs b/backend/src/routes.rs index d4e9ffe..d9a2e06 100644 --- a/backend/src/routes.rs +++ b/backend/src/routes.rs @@ -1,5 +1,5 @@ use askama_axum::Template; -use axum::{extract::State, response::IntoResponse, Extension}; +use axum::{extract::{Path, State}, response::IntoResponse, Extension}; use http::Request; use sqlx::SqlitePool; @@ -10,24 +10,51 @@ use crate::{HtmlTemplate, UserData}; struct ProfileTemplate { logged_in: bool, name: String, - family_name: String, - given_name: String, - email: String, + user: UserData } +/// Handles the profile page. pub async fn profile( Extension(user_data): Extension>, _request: Request, ) -> impl IntoResponse { + // Extract the user's name from the user data. + let user_name = user_data.as_ref().map(|s| s.name.clone()); + let logged_in = user_data.is_some(); + let name = user_name.unwrap_or_default(); + + // Extract the user data. + let user = user_data.as_ref().unwrap(); - let Some(UserData {id, email, family_name, given_name, name}) = user_data - else { - return HtmlTemplate(ProfileTemplate { logged_in: false, name: "".to_owned(), given_name: "".to_owned(), family_name: "".to_owned(), email: "".to_owned() }); - }; - let logged_in = id > 0; - - let template = ProfileTemplate { logged_in, name, given_name, family_name, email }; - HtmlTemplate(template) + // Create the profile template. + let template = ProfileTemplate { logged_in, name, user: user.clone() }; + return HtmlTemplate(template) +} + + pub async fn user_profile( + Path(user_id): Path, + State(db_pool): State, + Extension(user_data): Extension>, + _request: Request, +) -> impl IntoResponse { + // Extract the user's name from the user data. + let user_name = user_data.as_ref().map(|s| s.name.clone()); + let logged_in = user_data.is_some(); + let name = user_name.unwrap_or_default(); + + // Extract the user data. + let user = sqlx::query_as!( + UserData, + "SELECT * FROM users WHERE id = ?", + user_id + ) + .fetch_one(&db_pool) + .await + .unwrap(); + + // Create the profile template. + let template = ProfileTemplate { logged_in, name, user: user }; + return HtmlTemplate(template) } #[derive(Template)] diff --git a/backend/templates/base.html b/backend/templates/base.html index e210775..d8974cf 100644 --- a/backend/templates/base.html +++ b/backend/templates/base.html @@ -10,50 +10,50 @@ - - + + - - - {% block content %}{% endblock content %} - -
- - -
+
+ {% block content %}{% endblock content %} +
+
+
+ +
+

© 2024 Jean-Marie family

+
+
+
+ - -
\ No newline at end of file diff --git a/backend/templates/index.html b/backend/templates/index.html index 2b80b89..f9ab663 100644 --- a/backend/templates/index.html +++ b/backend/templates/index.html @@ -1,70 +1,85 @@ {% extends "base.html" %} {% block content %} {% if logged_in %} -
-
-

This will be the private information area for the extended Jean-Marie family.

-
+
+
+ +
+

This will be the private information area for the extended Jean-Marie family.

+

Web links

TLC Creations

Fonts

Family tree

+
+
+ +

Events

+
+
{% else %}