Change google_auth_return url to /
This commit is contained in:
parent
06a6811972
commit
042f6b17aa
|
|
@ -1,2 +1,4 @@
|
|||
backend/target
|
||||
backend/db
|
||||
backend/id_rsa
|
||||
backend/id_rsa.pub
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ pub async fn login(
|
|||
State(db_pool): State<SqlitePool>,
|
||||
Host(hostname): Host,
|
||||
) -> Result<Redirect, AppError> {
|
||||
|
||||
if user_data.is_some() {
|
||||
// check if already authenticated
|
||||
return Ok(Redirect::to("/"));
|
||||
|
|
@ -69,10 +68,9 @@ pub async fn login(
|
|||
|
||||
let return_url = params
|
||||
.remove("return_url")
|
||||
.unwrap_or_else(|| "/dashboard".to_string());
|
||||
.unwrap_or_else(|| "/".to_string());
|
||||
// TODO: check if return_url is valid
|
||||
println!("Return URL: {}", return_url);
|
||||
|
||||
|
||||
let client = get_client(hostname)?;
|
||||
|
||||
let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
|
||||
|
|
@ -103,10 +101,15 @@ pub async fn login(
|
|||
pub async fn google_auth_return(
|
||||
Query(mut params): Query<HashMap<String, String>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
cookie: Option<TypedHeader<Cookie>>,
|
||||
Host(hostname): Host,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let state = CsrfToken::new(params.remove("state").ok_or("OAuth: without state")?);
|
||||
let code = AuthorizationCode::new(params.remove("code").ok_or("OAuth: without code")?);
|
||||
let mut headers = axum::response::AppendHeaders([(
|
||||
axum::http::header::SET_COOKIE,
|
||||
"session_token=".to_owned() + "; path=/; httponly; secure; samesite=strict",
|
||||
)]);
|
||||
|
||||
let query: (String, String) = sqlx::query_as(
|
||||
r#"DELETE FROM oauth2_state_storage WHERE csrf_state = ? RETURNING pkce_code_verifier,return_url"#,
|
||||
|
|
@ -115,20 +118,8 @@ pub async fn google_auth_return(
|
|||
.fetch_one(&db_pool)
|
||||
.await?;
|
||||
|
||||
// Alternative:
|
||||
// let query: (String, String) = sqlx::query_as(
|
||||
// r#"SELECT pkce_code_verifier,return_url FROM oauth2_state_storage WHERE csrf_state = ?"#,
|
||||
// )
|
||||
// .bind(state.secret())
|
||||
// .fetch_one(&db_pool)
|
||||
// .await?;
|
||||
// let _ = sqlx::query("DELETE FROM oauth2_state_storage WHERE csrf_state = ?")
|
||||
// .bind(state.secret())
|
||||
// .execute(&db_pool)
|
||||
// .await;
|
||||
|
||||
let pkce_code_verifier = query.0;
|
||||
let return_url = query.1;
|
||||
let _return_url = query.1;
|
||||
let pkce_code_verifier = PkceCodeVerifier::new(pkce_code_verifier);
|
||||
|
||||
// Exchange the code with a token.
|
||||
|
|
@ -190,46 +181,61 @@ pub async fn google_auth_return(
|
|||
.bind(email.as_str())
|
||||
.fetch_one(&db_pool)
|
||||
.await;
|
||||
let user_id = if let Ok(query) = query {
|
||||
let user_id = if let Ok(query) = query
|
||||
{
|
||||
query.0
|
||||
} else {
|
||||
let query: (i64,) = sqlx::query_as("INSERT INTO users (email, name, family_name, given_name) VALUES (?, ?, ?, ?) RETURNING id")
|
||||
.bind(email)
|
||||
.bind(name)
|
||||
.bind(family_name)
|
||||
.bind(given_name)
|
||||
.bind(email.clone())
|
||||
.bind(name.clone())
|
||||
.bind(family_name.clone())
|
||||
.bind(given_name.clone())
|
||||
.fetch_one(&db_pool)
|
||||
.await?;
|
||||
|
||||
// Add public role
|
||||
sqlx::query("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)")
|
||||
.bind(query.0)
|
||||
.bind("1")
|
||||
.execute(&db_pool)
|
||||
.await?;
|
||||
|
||||
query.0
|
||||
};
|
||||
|
||||
// Create a session for the user
|
||||
let session_token_p1 = Uuid::new_v4().to_string();
|
||||
let session_token_p2 = Uuid::new_v4().to_string();
|
||||
let session_token = [session_token_p1.as_str(), "_", session_token_p2.as_str()].concat();
|
||||
let headers = axum::response::AppendHeaders([(
|
||||
axum::http::header::SET_COOKIE,
|
||||
"session_token=".to_owned()
|
||||
+ &*session_token
|
||||
+ "; path=/; httponly; secure; samesite=strict",
|
||||
)]);
|
||||
let now = Utc::now().timestamp();
|
||||
// Update session with user id or create new session
|
||||
if let Some(cookie) = cookie {
|
||||
if let Some(_session_token) = cookie.get("session_token") {
|
||||
} else {
|
||||
println!("google_auth_return : No session token");
|
||||
// Create a session for the user
|
||||
let session_token_p1 = Uuid::new_v4().to_string();
|
||||
let session_token_p2 = Uuid::new_v4().to_string();
|
||||
let session_token =
|
||||
[session_token_p1.as_str(), "_", session_token_p2.as_str()].concat();
|
||||
headers = axum::response::AppendHeaders([(
|
||||
axum::http::header::SET_COOKIE,
|
||||
"session_token=".to_owned()
|
||||
+ &*session_token
|
||||
+ "; path=/; httponly; secure; samesite=strict",
|
||||
)]);
|
||||
let now = Utc::now().timestamp();
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO user_sessions
|
||||
sqlx::query(
|
||||
"INSERT INTO user_sessions
|
||||
(session_token_p1, session_token_p2, user_id, created_at, expires_at)
|
||||
VALUES (?, ?, ?, ?, ?);",
|
||||
)
|
||||
.bind(session_token_p1)
|
||||
.bind(session_token_p2)
|
||||
.bind(user_id)
|
||||
.bind(now)
|
||||
.bind(now + 60 * 60 * 24)
|
||||
.execute(&db_pool)
|
||||
.await?;
|
||||
|
||||
println!("Returning to: {}", return_url);
|
||||
Ok((headers, Redirect::to(return_url.as_str())))
|
||||
)
|
||||
.bind(session_token_p1)
|
||||
.bind(session_token_p2)
|
||||
.bind(user_id) // Set user to anonymous
|
||||
.bind(now)
|
||||
.bind(now + 60 * 60 * 24)
|
||||
.execute(&db_pool)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok((headers, Redirect::to("/")))
|
||||
}
|
||||
|
||||
pub async fn logout(
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ mod routes;
|
|||
use error_handling::AppError;
|
||||
use middlewares::{check_auth, inject_user_data};
|
||||
use google_oauth::{login, logout, google_auth_return};
|
||||
use routes::{dashboard, index, profile, user_profile, useradmin};
|
||||
use routes::{dashboard, index, about, profile, user_profile, useradmin};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub db_pool: SqlitePool,
|
||||
pub db_pool: SqlitePool
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)]
|
||||
|
|
@ -42,7 +42,7 @@ async fn main() {
|
|||
.connect("sqlite://db/db.sqlite3")
|
||||
.await;
|
||||
|
||||
let app_state = AppState {db_pool: db_pool.expect("Failed to get db_pool") };
|
||||
let app_state = AppState {db_pool:db_pool.expect("Failed to get db_pool")};
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
|
|
@ -55,21 +55,18 @@ async fn main() {
|
|||
|
||||
// build our application with some routes
|
||||
let app = Router::new()
|
||||
//Routes that require authentication
|
||||
.route("/dashboard", get(dashboard))
|
||||
.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
|
||||
.nest_service("/assets", ServeDir::new("templates/assets")
|
||||
.fallback(get_service(ServeDir::new("templates/assets"))))
|
||||
.route("/", get(index))
|
||||
.route("/about", get(about))
|
||||
.route("/login", get(login))
|
||||
.route("/logout", get(logout))
|
||||
//.route("/google_auth", get(google_auth))
|
||||
.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(), inject_user_data))
|
||||
.with_state(app_state.db_pool)
|
||||
.layer(Extension(user_data))
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ pub async fn inject_user_data(
|
|||
mut request: Request<Body>,
|
||||
next: Next,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
println!("Injecting user data");
|
||||
println!("inject_user_data : Injecting user data");
|
||||
if let Some(cookie) = cookie {
|
||||
println!("{:#?}", cookie.get("session_token"));
|
||||
println!("inject_user_data : {:#?}", cookie.get("session_token"));
|
||||
if let Some(session_token) = cookie.get("session_token") {
|
||||
println!("Found session token: {}", 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 +46,7 @@ pub async fn inject_user_data(
|
|||
session_token_p2_db,
|
||||
) {
|
||||
let id = query.0;
|
||||
println!("Found user: {}", id);
|
||||
println!("inject_user_data : Found user: {}", id);
|
||||
let expires_at = query.1;
|
||||
if expires_at > Utc::now().timestamp() {
|
||||
let row = sqlx::query_as!(
|
||||
|
|
@ -65,7 +65,7 @@ pub async fn inject_user_data(
|
|||
given_name: row.given_name,
|
||||
}));
|
||||
} else {
|
||||
println!("Session expired");
|
||||
println!("inject_user_data : Session expired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,8 +82,8 @@ pub async fn check_auth(
|
|||
request: Request<Body>,
|
||||
next: Next,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
println!("check_auth");
|
||||
println!("{:#?}", request);
|
||||
println!("check_auth : Starting");
|
||||
//println!("{:#?}", request);
|
||||
if request
|
||||
.extensions()
|
||||
.get::<Option<UserData>>()
|
||||
|
|
@ -91,9 +91,8 @@ pub async fn check_auth(
|
|||
.is_some()
|
||||
{
|
||||
let path = &*request.uri().to_string();
|
||||
println!("{}", path);
|
||||
println!(
|
||||
"{}",
|
||||
"check_auth : {}",
|
||||
&*request
|
||||
.extensions()
|
||||
.get::<Option<UserData>>()
|
||||
|
|
@ -123,13 +122,14 @@ pub async fn check_auth(
|
|||
// 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 =?"#)
|
||||
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)"#)
|
||||
.bind(remaining_path.to_str().unwrap())
|
||||
.bind(request.extensions().get::<Option<UserData>>().unwrap().as_ref().unwrap().email.as_str())
|
||||
.fetch_one(&app_state)
|
||||
.await;
|
||||
if let Ok(query) = query {
|
||||
if query.0 != "" {
|
||||
println!("check_auth : found auth for {}", query.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -137,10 +137,8 @@ pub async fn check_auth(
|
|||
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 =?"#)
|
||||
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)"#)
|
||||
.bind(remaining_path.to_str().unwrap())
|
||||
.bind(request.extensions().get::<Option<UserData>>().unwrap().as_ref().unwrap().email.as_str())
|
||||
.fetch_one(&app_state)
|
||||
|
|
@ -158,7 +156,7 @@ pub async fn check_auth(
|
|||
0
|
||||
};
|
||||
|
||||
println!("{}", user_id);
|
||||
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
|
||||
|
|
@ -169,17 +167,11 @@ pub async fn check_auth(
|
|||
} 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();
|
||||
Ok(Redirect::to(login_url.as_str()).into_response())
|
||||
//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)
|
||||
}
|
||||
}
|
||||
|
||||
fn access_auth (_uri: &str, _userid: i64) {
|
||||
// Check uri and userid for access and build the menu for links to the allowed resources
|
||||
|
||||
let _user_auth = true;
|
||||
|
||||
let _menu: Vec<String> = vec![];
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use askama_axum::{Response, Template};
|
||||
use axum::{extract::{Path, State}, response::{Html, IntoResponse}, Extension};
|
||||
use http::{Request, StatusCode};
|
||||
use http::StatusCode;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::UserData;
|
||||
|
|
@ -45,23 +45,21 @@ struct DashboardTemplate {
|
|||
name: String,
|
||||
}
|
||||
|
||||
pub async fn index<T>(
|
||||
pub async fn index(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
_request: Request<T>,
|
||||
) -> impl IntoResponse {
|
||||
let user_name = user_data.map(|s| s.name);
|
||||
let logged_in = user_name.is_some();
|
||||
let name = user_name.unwrap_or_default();
|
||||
|
||||
println!("logged_in: {}, name: {}", logged_in, name);
|
||||
println!("index : logged_in = {}, name = {}", logged_in, name);
|
||||
|
||||
let template = IndexTemplate { logged_in, name };
|
||||
HtmlTemplate(template)
|
||||
}
|
||||
|
||||
pub async fn dashboard<T>(
|
||||
pub async fn dashboard(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
_request: Request<T>,
|
||||
) -> impl IntoResponse {
|
||||
let user_name = user_data.map(|s| s.name);
|
||||
let logged_in = user_name.is_some();
|
||||
|
|
@ -72,9 +70,8 @@ pub async fn dashboard<T>(
|
|||
}
|
||||
|
||||
/// Handles the profile page.
|
||||
pub async fn profile<T>(
|
||||
pub async fn profile(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
_request: Request<T>,
|
||||
) -> impl IntoResponse {
|
||||
// Extract the user's name from the user data.
|
||||
let user_name = user_data.as_ref().map(|s| s.name.clone());
|
||||
|
|
@ -89,11 +86,10 @@ pub async fn profile<T>(
|
|||
return HtmlTemplate(template)
|
||||
}
|
||||
|
||||
pub async fn user_profile<T>(
|
||||
pub async fn user_profile(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
_request: Request<T>,
|
||||
) -> impl IntoResponse {
|
||||
// Extract the user's name from the user data.
|
||||
let user_name = user_data.as_ref().map(|s| s.name.clone());
|
||||
|
|
@ -123,10 +119,9 @@ struct UserAdminTemplate {
|
|||
users: Vec<UserData>
|
||||
}
|
||||
|
||||
pub async fn useradmin<T>(
|
||||
pub async fn useradmin(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
_request: Request<T>,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let user_email = user_data.map(|s| s.email);
|
||||
|
|
@ -141,3 +136,22 @@ pub async fn useradmin<T>(
|
|||
let template = UserAdminTemplate { logged_in, name, users };
|
||||
HtmlTemplate(template)
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "about.html")]
|
||||
struct AboutTemplate {
|
||||
logged_in: bool,
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub async fn about(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
let user_name = user_data.map(|s| s.name);
|
||||
let logged_in = user_name.is_some();
|
||||
let name = user_name.unwrap_or_default();
|
||||
|
||||
let template = AboutTemplate { logged_in, name };
|
||||
HtmlTemplate(template)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}About{% endblock %}
|
||||
{% block content %}
|
||||
This is a demo OAuth website.
|
||||
This is will give more information about the website
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
#ssh www@192.168.59.11 'pkill jean-marie'
|
||||
scp -i id_rsa target/release/jean-marie www@192.168.59.11:/opt/jean-marie
|
||||
scp -i id_rsa .env www@192.168.59.11:/opt/jean-marie
|
||||
scp -i id_rsa -r templates www@192.168.59.11:/opt/jean-marie
|
||||
#ssh www@192.168.59.11 'cd /opt/jean-marie && ./jean-marie&'
|
||||
Loading…
Reference in New Issue