fix(auth): prevent password_hash from reaching the frontend
Introduce PublicUser struct without password_hash field. The get_current_user server function now returns PublicUser via CurrentUserResponse, so Argon2 hashes are never serialized to WASM. Internal server-side functions (get_current_admin_user) continue using the full User struct.
This commit is contained in:
parent
8146a8a779
commit
f5413e00cc
@ -6,7 +6,7 @@ use http::header::{HeaderValue, SET_COOKIE};
|
|||||||
|
|
||||||
use crate::auth::{password, session};
|
use crate::auth::{password, session};
|
||||||
use crate::db::pool::DB_POOL;
|
use crate::db::pool::DB_POOL;
|
||||||
use crate::models::user::{User, UserRole};
|
use crate::models::user::{PublicUser, User, UserRole};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn validate_username(username: &str) -> Result<(), String> {
|
fn validate_username(username: &str) -> Result<(), String> {
|
||||||
@ -261,7 +261,7 @@ pub async fn logout() -> Result<AuthResponse, ServerFnError> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct CurrentUserResponse {
|
pub struct CurrentUserResponse {
|
||||||
pub user: Option<User>,
|
pub user: Option<PublicUser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server(GetCurrentUser, "/api")]
|
#[server(GetCurrentUser, "/api")]
|
||||||
@ -289,7 +289,7 @@ pub async fn get_current_user() -> Result<CurrentUserResponse, ServerFnError> {
|
|||||||
|
|
||||||
let row = client
|
let row = client
|
||||||
.query_opt(
|
.query_opt(
|
||||||
"SELECT u.id, u.username, u.email, u.password_hash, u.role, u.created_at
|
"SELECT u.id, u.username, u.email, u.role, u.created_at
|
||||||
FROM sessions s
|
FROM sessions s
|
||||||
JOIN users u ON s.user_id = u.id
|
JOIN users u ON s.user_id = u.id
|
||||||
WHERE s.token = $1 AND s.expires_at > NOW()",
|
WHERE s.token = $1 AND s.expires_at > NOW()",
|
||||||
@ -305,11 +305,10 @@ pub async fn get_current_user() -> Result<CurrentUserResponse, ServerFnError> {
|
|||||||
Some(row) => {
|
Some(row) => {
|
||||||
let role_str: String = row.get("role");
|
let role_str: String = row.get("role");
|
||||||
let role = UserRole::from_str(&role_str).unwrap_or(UserRole::Blocked);
|
let role = UserRole::from_str(&role_str).unwrap_or(UserRole::Blocked);
|
||||||
Some(User {
|
Some(PublicUser {
|
||||||
id: row.get("id"),
|
id: row.get("id"),
|
||||||
username: row.get("username"),
|
username: row.get("username"),
|
||||||
email: row.get("email"),
|
email: row.get("email"),
|
||||||
password_hash: row.get("password_hash"),
|
|
||||||
role,
|
role,
|
||||||
created_at: row.get("created_at"),
|
created_at: row.get("created_at"),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::models::user::User;
|
use crate::models::user::PublicUser;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct UserContext {
|
pub struct UserContext {
|
||||||
pub user: Signal<Option<Arc<User>>>,
|
pub user: Signal<Option<Arc<PublicUser>>>,
|
||||||
pub checked: Signal<bool>,
|
pub checked: Signal<bool>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,3 +27,24 @@ pub struct User {
|
|||||||
pub role: UserRole,
|
pub role: UserRole,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PublicUser {
|
||||||
|
pub id: i32,
|
||||||
|
pub username: String,
|
||||||
|
pub email: String,
|
||||||
|
pub role: UserRole,
|
||||||
|
pub created_at: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<User> for PublicUser {
|
||||||
|
fn from(u: User) -> Self {
|
||||||
|
PublicUser {
|
||||||
|
id: u.id,
|
||||||
|
username: u.username,
|
||||||
|
email: u.email,
|
||||||
|
role: u.role,
|
||||||
|
created_at: u.created_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@ pub fn AppRouter() -> Element {
|
|||||||
Theme::Light => "",
|
Theme::Light => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = use_signal(|| None::<Arc<crate::models::user::User>>);
|
let user = use_signal(|| None::<Arc<crate::models::user::PublicUser>>);
|
||||||
let checked = use_signal(|| false);
|
let checked = use_signal(|| false);
|
||||||
use_context_provider(|| UserContext { user, checked });
|
use_context_provider(|| UserContext { user, checked });
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user