This commit is contained in:
2026-06-21 19:11:23 +02:00
parent 3fd2b8ade8
commit e9d77fbd05
11 changed files with 96 additions and 77 deletions
+23 -15
View File
@@ -91,30 +91,38 @@ pub async fn auth_middleware(
mut req: Request<Body>,
next: Next,
) -> Response {
// Extraction du JWT depuis le header Authorization
let user: Option<CurrentUser> = match req
// Extraction du JWT : d'abord via le header Authorization, sinon via la query string "token"
let token = req
.headers()
.get(header::AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.and_then(|auth_header| {
if auth_header.starts_with("Bearer ") {
Some(&auth_header[7..])
Some(auth_header[7..].to_string())
} else {
None
}
})
.and_then(|token| verify_jwt(token, &app_state.config.jwt.secret).ok())
{
Some(claims) => app_state
.repositories
.user
.get_by_id(claims.user_id)
.await
.ok()
.flatten()
.map(CurrentUser),
None => None,
};
.or_else(|| {
req.uri().query().and_then(|q| {
form_urlencoded::parse(q.as_bytes())
.find(|(key, _)| key == "token")
.map(|(_, value)| value.into_owned())
})
});
let user: Option<CurrentUser> =
match token.and_then(|t| verify_jwt(&t, &app_state.config.jwt.secret).ok()) {
Some(claims) => app_state
.repositories
.user
.get_by_id(claims.user_id)
.await
.ok()
.flatten()
.map(CurrentUser),
None => None,
};
// Mise à jour du RequestContext existant
if let Some(user) = &user {
+15 -1
View File
@@ -4,7 +4,7 @@ use sea_orm::entity::prelude::*;
use sea_orm::prelude::async_trait::async_trait;
use sea_orm::Set;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "user")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
@@ -18,6 +18,20 @@ pub struct Model {
pub is_superuser: bool,
}
impl std::fmt::Debug for Model {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("User")
.field("id", &self.id)
.field("username", &self.username)
.field("password", &"<redacted>")
.field("pub_key", &self.pub_key)
.field("created_at", &self.created_at)
.field("updated_at", &self.updated_at)
.field("is_superuser", &self.is_superuser)
.finish()
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::channel_user::Entity")]
+17 -4
View File
@@ -34,8 +34,15 @@ impl UserRepository {
pub async fn check_password(&self, username: &str, password: &str) -> AnyResult<user::Model> {
let user = self.get_by_username(username.to_string()).await?;
if let Some(user) = user {
let password_ok = password::verify_password(password, user.password.as_str())
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
let password_to_verify = password.to_string();
let hash_to_verify = user.password.clone();
let password_ok = tokio::task::spawn_blocking(move || {
password::verify_password(&password_to_verify, &hash_to_verify)
})
.await
.map_err(|e| anyhow::anyhow!("Join error: {}", e))?
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
if password_ok {
return Ok(user);
@@ -63,8 +70,14 @@ impl UserRepository {
.ok_or_else(|| anyhow::anyhow!("User not found"))?;
let mut active = user.into_active_model();
let password = password::hash_password(&password)
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
let password_to_hash = password.clone();
let password =
tokio::task::spawn_blocking(move || password::hash_password(&password_to_hash))
.await
.map_err(|e| anyhow::anyhow!("Join error: {}", e))?
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
active.password = Set(password);
let user = self.update(active).await?;
+5 -50
View File
@@ -1,4 +1,3 @@
use crate::auth::token::verify_jwt;
use crate::core::AppState;
use crate::http::context::CurrentUser;
use crate::models::user::Model as User;
@@ -13,7 +12,6 @@ use axum::{
use futures_util::{sink::SinkExt, stream::StreamExt};
use serde::Deserialize;
use tokio::sync::mpsc;
use uuid::Uuid;
#[derive(Deserialize)]
pub struct WsQuery {
@@ -49,9 +47,6 @@ async fn handle_socket(socket: WebSocket, state: AppState, user: User) {
client.on_connect().await;
state.gateway.add_client(client.clone());
// // Enregistrement du client (Connect)
// on_connect(user_id, tx, &state).await;
//
// Task pour envoyer les messages du canal mpsc vers le WebSocket
let mut send_task = tokio::spawn(async move {
while let Some(message) = rx.recv().await {
@@ -60,7 +55,7 @@ async fn handle_socket(socket: WebSocket, state: AppState, user: User) {
}
}
});
//
// Task pour recevoir les messages du WebSocket
let client_clone = client.clone();
let state_clone = state.clone();
@@ -69,54 +64,14 @@ async fn handle_socket(socket: WebSocket, state: AppState, user: User) {
client_clone.on_message(message).await;
}
});
//
// Attente de la fin d'une des tâches (déconnexion)
tokio::select! {
_ = (&mut send_task) => recv_task.abort(),
_ = (&mut recv_task) => send_task.abort(),
};
//
state.gateway.remove_client(client.clone());
// // Déconnexion (Disconnect)
// on_disconnect(user_id, &state).await;
}
pub async fn on_connect(user_id: Uuid, tx: mpsc::UnboundedSender<Message>, state: &AppState) {
tracing::info!("Client connected: {}", user_id);
let mut clients = state
.gateway
.clients
.write()
.expect("Failed to lock clients for writing");
clients.insert(user_id, GatewayClient { user_id, tx });
}
pub async fn on_disconnect(user_id: Uuid, state: &AppState) {
tracing::info!("Client disconnected: {}", user_id);
let mut clients = state
.gateway
.clients
.write()
.expect("Failed to lock clients for writing");
clients.remove(&user_id);
}
pub async fn on_message(user_id: Uuid, message: Message, _state: &AppState) {
tracing::debug!("Message received from {}: {:?}", user_id, message);
// Exemple d'utilisation de l'état/repositories
// let user_opt = state.repositories.user.get_by_id(user_id).await.ok().flatten();
match message {
Message::Text(text) => {
tracing::info!("Received text from {}: {}", user_id, text);
// Logique de dispatch ou de traitement ici
}
Message::Binary(_) => {
tracing::info!("Received binary from {}", user_id);
}
Message::Close(_) => {
tracing::info!("Received close from {}", user_id);
}
_ => {}
}
client.on_disconnect().await;
}
+6 -2
View File
@@ -49,9 +49,13 @@ impl GatewayClient {
}
}
async fn on_connect(&self) {}
async fn on_connect(&self) {
tracing::info!("Client connected: {:?}", self.user);
}
async fn on_disconnect(&self) {}
async fn on_disconnect(&self) {
tracing::info!("Client disconnected: {:?}", self.user);
}
async fn on_message(&self, message: Message) {
match message {
+4 -2
View File
@@ -31,10 +31,12 @@ pub fn router() -> OxRouter {
let api_routes = Router::new()
.merge(secure_routes)
.merge(auth::routes::router())
.merge(core::routes::router())
.merge(gateway::routes::router());
.merge(core::routes::router());
let ws_routes = Router::new().merge(gateway::routes::router());
Router::new()
.nest("/api", api_routes)
.nest("/ws", ws_routes)
.merge(SwaggerUi::new("/swagger").url("/api-docs/openapi.json", openapi::ApiDoc::openapi()))
}