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::db::pool::DB_POOL;
|
||||
use crate::models::user::{User, UserRole};
|
||||
use crate::models::user::{PublicUser, User, UserRole};
|
||||
|
||||
#[allow(dead_code)]
|
||||
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)]
|
||||
pub struct CurrentUserResponse {
|
||||
pub user: Option<User>,
|
||||
pub user: Option<PublicUser>,
|
||||
}
|
||||
|
||||
#[server(GetCurrentUser, "/api")]
|
||||
@ -289,7 +289,7 @@ pub async fn get_current_user() -> Result<CurrentUserResponse, ServerFnError> {
|
||||
|
||||
let row = client
|
||||
.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
|
||||
JOIN users u ON s.user_id = u.id
|
||||
WHERE s.token = $1 AND s.expires_at > NOW()",
|
||||
@ -305,11 +305,10 @@ pub async fn get_current_user() -> Result<CurrentUserResponse, ServerFnError> {
|
||||
Some(row) => {
|
||||
let role_str: String = row.get("role");
|
||||
let role = UserRole::from_str(&role_str).unwrap_or(UserRole::Blocked);
|
||||
Some(User {
|
||||
Some(PublicUser {
|
||||
id: row.get("id"),
|
||||
username: row.get("username"),
|
||||
email: row.get("email"),
|
||||
password_hash: row.get("password_hash"),
|
||||
role,
|
||||
created_at: row.get("created_at"),
|
||||
})
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::models::user::User;
|
||||
use crate::models::user::PublicUser;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UserContext {
|
||||
pub user: Signal<Option<Arc<User>>>,
|
||||
pub user: Signal<Option<Arc<PublicUser>>>,
|
||||
pub checked: Signal<bool>,
|
||||
}
|
||||
|
||||
@ -27,3 +27,24 @@ pub struct User {
|
||||
pub role: UserRole,
|
||||
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 => "",
|
||||
};
|
||||
|
||||
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);
|
||||
use_context_provider(|| UserContext { user, checked });
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user