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;
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 routes::{dashboard, index, about, profile, user_profile, useradmin};
@ -66,7 +66,7 @@ async fn main() {
.route("/login", get(login))
.route("/logout", get(logout))
.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))
.with_state(app_state.db_pool)
.layer(Extension(user_data))

View File

@ -6,7 +6,7 @@ use axum::{
extract::State,
http::Request,
middleware::Next,
response::{IntoResponse, Redirect},
response::IntoResponse,
};
use axum_extra::TypedHeader;
use chrono::Utc;
@ -19,11 +19,8 @@ pub async fn inject_user_data(
mut request: Request<Body>,
next: Next,
) -> Result<impl IntoResponse, AppError> {
println!("inject_user_data : Injecting user data");
if let Some(cookie) = cookie {
println!("inject_user_data : {:#?}", 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 query: Result<(i64, i64, String), _> = sqlx::query_as(
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,
) {
let id = query.0;
println!("inject_user_data : Found user: {}", id);
let expires_at = query.1;
if expires_at > Utc::now().timestamp() {
let row = sqlx::query_as!(
@ -62,10 +58,8 @@ pub async fn inject_user_data(
email: row.email,
name: row.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)
}
pub async fn check_auth(
State(app_state): State<SqlitePool>,
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
);
pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: SqlitePool) -> bool {
if let Some(user_data) = user_data {
let query: Result<(i64,), _> = match path {
"/profile" => {
sqlx::query_as(r#"select u.id from users u where email =?"#)
.bind(
request
.extensions()
.get::<Option<UserData>>()
.unwrap()
.as_ref()
.unwrap()
.email
.as_str(),
)
.fetch_one(&app_state)
.await
return true;
}
_ => {
// 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 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(request.extensions().get::<Option<UserData>>().unwrap().as_ref().unwrap().email.as_str())
.fetch_one(&app_state)
.fetch_one(&db_pool)
.await;
if let Ok(query) = query {
if query.0 != "" {
println!("check_auth : found auth for {}", query.0);
break;
}
}
@ -138,40 +95,19 @@ pub async fn check_auth(
}
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(request.extensions().get::<Option<UserData>>().unwrap().as_ref().unwrap().email.as_str())
.fetch_one(&app_state)
.bind(user_data.email.as_str())
.fetch_one(&db_pool)
.await
}
};
// user is logged in
// check if user has the proper role
// if not, display banner and return to home page
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)
if let Ok(query) = query {
if query.0 == user_data.id {
return true;
}
}
}
return false;
}

View File

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

View File

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