Change from auth layer to using is_authorized function

This commit is contained in:
Chris Jean-Marie 2024-10-05 01:12:29 +00:00
parent 042f6b17aa
commit ef9951fcbe
4 changed files with 65 additions and 109 deletions

View File

@ -13,7 +13,7 @@ mod middlewares;
mod routes; mod routes;
use error_handling::AppError; use error_handling::AppError;
use middlewares::{check_auth, inject_user_data}; use middlewares::inject_user_data;
use google_oauth::{login, logout, google_auth_return}; use google_oauth::{login, logout, google_auth_return};
use routes::{dashboard, index, about, profile, user_profile, useradmin}; use routes::{dashboard, index, about, profile, user_profile, useradmin};
@ -66,7 +66,7 @@ async fn main() {
.route("/login", get(login)) .route("/login", get(login))
.route("/logout", get(logout)) .route("/logout", get(logout))
.route("/google_auth_return", get(google_auth_return)) .route("/google_auth_return", get(google_auth_return))
.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), check_auth)) //.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), check_auth))
.route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), inject_user_data)) .route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), inject_user_data))
.with_state(app_state.db_pool) .with_state(app_state.db_pool)
.layer(Extension(user_data)) .layer(Extension(user_data))

View File

@ -6,7 +6,7 @@ use axum::{
extract::State, extract::State,
http::Request, http::Request,
middleware::Next, middleware::Next,
response::{IntoResponse, Redirect}, response::IntoResponse,
}; };
use axum_extra::TypedHeader; use axum_extra::TypedHeader;
use chrono::Utc; use chrono::Utc;
@ -19,11 +19,8 @@ pub async fn inject_user_data(
mut request: Request<Body>, mut request: Request<Body>,
next: Next, next: Next,
) -> Result<impl IntoResponse, AppError> { ) -> Result<impl IntoResponse, AppError> {
println!("inject_user_data : Injecting user data");
if let Some(cookie) = cookie { if let Some(cookie) = cookie {
println!("inject_user_data : {:#?}", cookie.get("session_token"));
if let Some(session_token) = cookie.get("session_token") { if let Some(session_token) = cookie.get("session_token") {
println!("inject_user_data : Found session token: {}", session_token);
let session_token: Vec<&str> = session_token.split('_').collect(); let session_token: Vec<&str> = session_token.split('_').collect();
let query: Result<(i64, i64, String), _> = sqlx::query_as( let query: Result<(i64, i64, String), _> = sqlx::query_as(
r#"SELECT user_id,expires_at,session_token_p2 FROM user_sessions WHERE session_token_p1=?"#, r#"SELECT user_id,expires_at,session_token_p2 FROM user_sessions WHERE session_token_p1=?"#,
@ -46,7 +43,6 @@ pub async fn inject_user_data(
session_token_p2_db, session_token_p2_db,
) { ) {
let id = query.0; let id = query.0;
println!("inject_user_data : Found user: {}", id);
let expires_at = query.1; let expires_at = query.1;
if expires_at > Utc::now().timestamp() { if expires_at > Utc::now().timestamp() {
let row = sqlx::query_as!( let row = sqlx::query_as!(
@ -62,10 +58,8 @@ pub async fn inject_user_data(
email: row.email, email: row.email,
name: row.name, name: row.name,
family_name: row.family_name, family_name: row.family_name,
given_name: row.given_name, given_name: row.given_name
})); }));
} else {
println!("inject_user_data : Session expired");
} }
} }
} }
@ -77,59 +71,22 @@ pub async fn inject_user_data(
Ok(next.run(request).await) Ok(next.run(request).await)
} }
pub async fn check_auth( pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: SqlitePool) -> bool {
State(app_state): State<SqlitePool>, if let Some(user_data) = user_data {
request: Request<Body>,
next: Next,
) -> Result<impl IntoResponse, AppError> {
println!("check_auth : Starting");
//println!("{:#?}", request);
if request
.extensions()
.get::<Option<UserData>>()
.ok_or("check_auth: extensions have no UserData")?
.is_some()
{
let path = &*request.uri().to_string();
println!(
"check_auth : {}",
&*request
.extensions()
.get::<Option<UserData>>()
.unwrap()
.as_ref()
.unwrap()
.email
);
let query: Result<(i64,), _> = match path { let query: Result<(i64,), _> = match path {
"/profile" => { "/profile" => {
sqlx::query_as(r#"select u.id from users u where email =?"#) return true;
.bind(
request
.extensions()
.get::<Option<UserData>>()
.unwrap()
.as_ref()
.unwrap()
.email
.as_str(),
)
.fetch_one(&app_state)
.await
} }
_ => { _ => {
// loop through path to find a permission // loop through path to find a permission
let mut remaining_path = Path::new(path); let mut remaining_path = Path::new(path);
loop { 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 left join users u on u.id = ur.user_id where item = ? and (email = ? or r.role_id = 1)"#) 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 left join users u on u.id = ur.user_id where item = ?"#)
.bind(remaining_path.to_str().unwrap()) .bind(remaining_path.to_str().unwrap())
.bind(request.extensions().get::<Option<UserData>>().unwrap().as_ref().unwrap().email.as_str()) .fetch_one(&db_pool)
.fetch_one(&app_state)
.await; .await;
if let Ok(query) = query { if let Ok(query) = query {
if query.0 != "" { if query.0 != "" {
println!("check_auth : found auth for {}", query.0);
break; break;
} }
} }
@ -138,40 +95,19 @@ pub async fn check_auth(
} }
remaining_path = remaining_path.parent().unwrap(); remaining_path = remaining_path.parent().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 = ? or r.role_id = 1)"#) 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(remaining_path.to_str().unwrap())
.bind(request.extensions().get::<Option<UserData>>().unwrap().as_ref().unwrap().email.as_str()) .bind(user_data.email.as_str())
.fetch_one(&app_state) .fetch_one(&db_pool)
.await .await
} }
}; };
// user is logged in if let Ok(query) = query {
// check if user has the proper role if query.0 == user_data.id {
// if not, display banner and return to home page return true;
let user_id = if let Ok(query) = query { }
query.0
} else {
// user does not have the proper role
0
};
println!("check_auth : Authenticated user {}", user_id);
if user_id == 0 {
// user does not have the proper role
// display banner and return to home page
return Ok(Redirect::to("/").into_response());
} }
Ok(next.run(request).await)
} else {
// user is not logged in
// redirect to login page with return_url
//let login_url = "/login?return_url=".to_owned() + &*request.uri().to_string();
//let login_url = "/login".to_owned();
//Ok(Redirect::to(login_url.as_str()).into_response())
//println!("check_auth : {:#?}", request.headers().get("referer").unwrap());
println!("check_auth : No user data in the request");
Ok(next.run(request).await)
} }
} return false;
}

View File

@ -1,9 +1,9 @@
use askama_axum::{Response, Template}; use askama_axum::{Response, Template};
use axum::{extract::{Path, State}, response::{Html, IntoResponse}, Extension}; use axum::{extract::{Path, State}, response::{Html, IntoResponse, Redirect}, Extension};
use http::StatusCode; use http::StatusCode;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::UserData; use crate::{middlewares::is_authorized, UserData};
#[derive(Template)] #[derive(Template)]
#[template(path = "profile.html")] #[template(path = "profile.html")]
@ -46,44 +46,57 @@ struct DashboardTemplate {
} }
pub async fn index( pub async fn index(
State(db_pool): State<SqlitePool>,
Extension(user_data): Extension<Option<UserData>>, Extension(user_data): Extension<Option<UserData>>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let user_name = user_data.map(|s| s.name); let user_name = user_data.as_ref().map(|s| s.name.clone());
let logged_in = user_name.is_some(); let logged_in = user_name.is_some();
let name = user_name.unwrap_or_default(); let name = user_name.unwrap_or_default();
println!("index : logged_in = {}, name = {}", logged_in, name); if is_authorized("/dashboard", user_data, db_pool).await {
Redirect::to("/dashboard").into_response()
let template = IndexTemplate { logged_in, name }; } else {
HtmlTemplate(template) let template = IndexTemplate { logged_in, name };
HtmlTemplate(template).into_response()
}
} }
pub async fn dashboard( pub async fn dashboard(
State(db_pool): State<SqlitePool>,
Extension(user_data): Extension<Option<UserData>>, Extension(user_data): Extension<Option<UserData>>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let user_name = user_data.map(|s| s.name); let user_name = user_data.as_ref().map(|s| s.name.clone());
let logged_in = user_name.is_some(); let logged_in = user_name.is_some();
let name = user_name.unwrap_or_default(); let name = user_name.unwrap_or_default();
let template = DashboardTemplate { logged_in, name}; if is_authorized("/dashboard", user_data, db_pool).await {
HtmlTemplate(template) let template = DashboardTemplate { logged_in, name};
HtmlTemplate(template).into_response()
} else {
Redirect::to("/").into_response()
}
} }
/// Handles the profile page. /// Handles the profile page.
pub async fn profile( pub async fn profile(
State(db_pool): State<SqlitePool>,
Extension(user_data): Extension<Option<UserData>>, Extension(user_data): Extension<Option<UserData>>,
) -> impl IntoResponse { ) -> impl IntoResponse {
// Extract the user's name from the user data. // Extract the user's name from the user data.
let user_name = user_data.as_ref().map(|s| s.name.clone()); let user_name = user_data.as_ref().map(|s| s.name.clone());
let logged_in = user_data.is_some(); let logged_in = user_name.is_some();
let name = user_name.unwrap_or_default(); let name = user_name.unwrap_or_default();
// Extract the user data. // Extract the user data.
let user = user_data.as_ref().unwrap(); let user = user_data.as_ref().unwrap().clone();
// Create the profile template. if is_authorized("/profile", user_data, db_pool).await {
let template = ProfileTemplate { logged_in, name, user: user.clone() }; // Create the profile template.
return HtmlTemplate(template) let template = ProfileTemplate { logged_in, name, user: user.clone() };
return HtmlTemplate(template).into_response()
} else {
Redirect::to("/").into_response()
}
} }
pub async fn user_profile( pub async fn user_profile(
@ -106,9 +119,13 @@ pub async fn profile(
.await .await
.unwrap(); .unwrap();
// Create the profile template. if is_authorized("/users", user_data, db_pool).await {
let template = ProfileTemplate { logged_in, name, user: user }; // Create the profile template.
return HtmlTemplate(template) let template = ProfileTemplate { logged_in, name, user: user };
return HtmlTemplate(template).into_response()
} else {
Redirect::to("/").into_response()
}
} }
#[derive(Template)] #[derive(Template)]
@ -124,17 +141,21 @@ pub async fn useradmin(
State(db_pool): State<SqlitePool>, State(db_pool): State<SqlitePool>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let user_email = user_data.map(|s| s.email); let user_name = user_data.as_ref().map(|s| s.name.clone());
let logged_in = user_email.is_some(); let logged_in = user_name.is_some();
let name = user_email.unwrap_or_default(); let name = user_name.unwrap_or_default();
let users = sqlx::query_as::<_, UserData>("SELECT * FROM users") let users = sqlx::query_as::<_, UserData>("SELECT * FROM users")
.fetch_all(&db_pool) .fetch_all(&db_pool)
.await .await
.unwrap(); .unwrap();
let template = UserAdminTemplate { logged_in, name, users }; if is_authorized("/useradmin", user_data, db_pool).await {
HtmlTemplate(template) let template = UserAdminTemplate { logged_in, name, users };
HtmlTemplate(template).into_response()
} else {
Redirect::to("/").into_response()
}
} }
#[derive(Template)] #[derive(Template)]

View File

@ -2,10 +2,10 @@
{% block content %} {% block content %}
{% if logged_in %} {% if logged_in %}
<!-- Redirect to dashboard --> <!-- Redirect to dashboard -->
<script> <!-- Only if user account is authorized -->
window.location.href = "/dashboard"; <h2>Your account has not yet been verified</h2>
</script>
{% else %} {% else %}
{% endif %}
<!-- Carousel --> <!-- Carousel -->
<div id="demo" class="carousel slide" data-bs-ride="carousel"> <div id="demo" class="carousel slide" data-bs-ride="carousel">
@ -50,5 +50,4 @@
</button> </button>
</div> </div>
</div> </div>
{% endif %}
{% endblock content %} {% endblock content %}