veza/veza-chat-server/src/message_handler.rs
senke b103a09a25 chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 16:43:21 +01:00

328 lines
No EOL
11 KiB
Rust

//! Module de gestion des messages avec filtrage de contenu et sécurité
//!
//! Ce module fournit une couche de haut niveau pour traiter les messages entrants,
//! appliquer les filtres de sécurité et déléguer aux modules métier appropriés.
use std::sync::Arc;
use tracing::{info, warn};
use uuid::Uuid;
use crate::error::{ChatError, Result};
use crate::hub::common::ChatHub;
use crate::permissions::Role;
use crate::security::{EnhancedSecurity, SecurityAction, ContentFilter};
use crate::security::permission::PermissionService;
/// Gestionnaire centralisé pour tous les types de messages
pub struct MessageHandler {
security: EnhancedSecurity,
content_filter: ContentFilter,
hub: Arc<ChatHub>,
permission_service: Arc<PermissionService>,
}
impl MessageHandler {
pub fn new(hub: Arc<ChatHub>, permission_service: Arc<PermissionService>) -> Result<Self> {
Ok(Self {
security: EnhancedSecurity::new()?,
content_filter: ContentFilter::new()?,
hub,
permission_service,
})
}
/// Gère les messages de salon avec permissions
pub async fn handle_room_message(
&mut self,
user_id: Uuid,
username: &str,
room: &str,
content: &str,
session_token: &str,
user_ip: &str,
parent_id: Option<Uuid>,
) -> Result<()> {
// Validation de sécurité
self.security.validate_request(
user_id,
user_ip,
session_token,
&SecurityAction::SendMessage,
Some(content)
).await?;
// Filtrage du contenu
let clean_room = self.content_filter.validate_content(room)?;
let clean_content = self.content_filter.validate_content(content)?;
info!(
user_id = %user_id,
username = %username,
room = %clean_room,
content_length = %clean_content.len(),
"📝 Message de salon filtré et validé"
);
// Délégation à la logique métier
let room_id = self.get_room_id_by_name(&clean_room).await?;
// Vérifier les permissions avant d'envoyer le message
self.permission_service
.can_send_message(user_id, room_id)
.await
.map_err(|e| {
warn!(
user_id = %user_id,
room_id = %room_id,
error = %e,
"Permission refusée pour l'envoi de message dans le salon"
);
e
})?;
crate::hub::channels::send_room_message(&self.hub, room_id, user_id, username, &clean_content, parent_id, None).await?;
Ok(())
}
/// Gère les messages directs avec permissions
pub async fn handle_direct_message(
&mut self,
from_user: Uuid,
from_username: &str,
to_user: Uuid,
content: &str,
session_token: &str,
user_ip: &str,
parent_id: Option<Uuid>,
) -> Result<()> {
// Validation de sécurité
self.security.validate_request(
from_user,
user_ip,
session_token,
&SecurityAction::SendDM,
Some(content)
).await?;
// Filtrage du contenu
let clean_content = self.content_filter.validate_content(content)?;
info!(
from_user = %from_user,
from_username = %from_username,
to_user = %to_user,
content_length = %clean_content.len(),
"💬 Message direct filtré et validé"
);
// Délégation à la logique métier
let conversation_id = self.get_or_create_conversation(from_user, to_user).await?;
// Vérifier les permissions avant d'envoyer le message
self.permission_service
.can_send_message(from_user, conversation_id)
.await
.map_err(|e| {
warn!(
from_user = %from_user,
conversation_id = %conversation_id,
error = %e,
"Permission refusée pour l'envoi de message direct"
);
e
})?;
crate::hub::direct_messages::send_dm_message(&self.hub, conversation_id, from_user, from_username, &clean_content, parent_id, None).await?;
Ok(())
}
/// Gère la jointure d'un salon avec permissions
pub async fn handle_join_room(
&mut self,
user_id: Uuid,
username: &str,
room: &str,
session_token: &str,
user_ip: &str,
) -> Result<()> {
// Validation de sécurité
self.security.validate_request(
user_id,
user_ip,
session_token,
&SecurityAction::JoinRoom,
None
).await?;
// Validation du nom de salon
let clean_room = self.content_filter.validate_content(room)?;
// Vérification que le salon existe ou peut être créé
// Pour l'instant, on suppose que le salon existe
let room_exists = true;
if !room_exists {
return Err(ChatError::not_found("Salon", &clean_room));
}
info!(
user_id = %user_id,
username = %username,
room = %clean_room,
"🚪 Jointure de salon validée"
);
// Délégation à la logique métier
let room_id = self.get_room_id_by_name(&clean_room).await?;
crate::hub::channels::join_room(&self.hub, room_id, user_id).await?;
// Envoi de confirmation
Ok(())
}
/// Gère la récupération d'historique avec permissions
pub async fn handle_room_history(
&mut self,
user_id: Uuid,
user_role: &Role,
room: &str,
limit: Option<i32>,
session_token: &str,
user_ip: &str,
) -> Result<Vec<crate::hub::channels::RoomMessage>> {
// Validation de sécurité pour la lecture
self.security.validate_request(
user_id,
user_ip,
session_token,
&SecurityAction::SendMessage, // Approximation
None
).await?;
// Validation du nom de salon
let clean_room = self.content_filter.validate_content(room)?;
let limit = limit.unwrap_or(50).min(100); // Limiter à 100 messages max
// Vérification des permissions de lecture
if !self.can_read_room_history(user_id, user_role, &clean_room).await? {
return Err(ChatError::unauthorized("Lecture de l'historique du salon"));
}
// Délégation à la logique métier
let room_id = self.get_room_id_by_name(&clean_room).await?;
let messages = crate::hub::channels::fetch_room_history(&self.hub, room_id, user_id, limit as i64, None).await?;
// Envoi de la réponse
info!(
user_id = %user_id,
room = %clean_room,
message_count = %messages.len(),
"📚 Historique de salon récupéré"
);
Ok(messages)
}
/// Gère la récupération d'historique DM avec permissions
pub async fn handle_dm_history(
&mut self,
user_id: Uuid,
with_user: Uuid,
limit: Option<i32>,
session_token: &str,
user_ip: &str,
) -> Result<Vec<crate::hub::direct_messages::DmMessage>> {
// Validation de sécurité
self.security.validate_request(
user_id,
user_ip,
session_token,
&SecurityAction::SendDM,
None
).await?;
let limit = limit.unwrap_or(50).min(100);
// Vérification que l'utilisateur peut lire cette conversation
if !self.can_read_dm_conversation(user_id, with_user).await? {
return Err(ChatError::unauthorized("Lecture de conversation privée"));
}
// Délégation à la logique métier
let conversation_id = self.get_or_create_conversation(user_id, with_user).await?;
let messages = crate::hub::direct_messages::fetch_history(&self.hub, conversation_id, user_id, limit as i64, None).await?;
// Envoi de la réponse
info!(
user_id = %user_id,
with_user = %with_user,
message_count = %messages.len(),
"💬 Historique DM récupéré"
);
Ok(messages)
}
/// Vérifie si un utilisateur peut lire l'historique d'un salon
async fn can_read_room_history(&self, user_id: Uuid, user_role: &Role, room: &str) -> Result<bool> {
// Logique simple : les admins et modérateurs peuvent tout lire
match user_role {
Role::Admin | Role::Moderator | Role::SuperAdmin => Ok(true),
Role::User => {
// Les utilisateurs normaux peuvent lire les salons dont ils sont membres
let room_id = self.get_room_id_by_name(room).await?;
self.permission_service
.can_read_conversation(user_id, room_id)
.await
.map(|_| true)
.or_else(|e| {
warn!(
user_id = %user_id,
room = %room,
error = %e,
"Permission refusée pour la lecture de l'historique"
);
Ok(false)
})
}
_ => Ok(false),
}
}
/// Vérifie si un utilisateur peut lire une conversation DM
async fn can_read_dm_conversation(&self, user_id: Uuid, with_user: Uuid) -> Result<bool> {
// Un utilisateur peut lire ses propres conversations
if user_id == with_user {
return Ok(true);
}
// Récupérer ou créer la conversation entre les deux utilisateurs
let conversation_id = self.get_or_create_conversation(user_id, with_user).await?;
// Vérifier les permissions
self.permission_service
.can_read_conversation(user_id, conversation_id)
.await
.map(|_| true)
.or_else(|e| {
warn!(
user_id = %user_id,
with_user = %with_user,
conversation_id = %conversation_id,
error = %e,
"Permission refusée pour la lecture de la conversation DM"
);
Ok(false)
})
}
/// Récupère ou crée une conversation entre deux utilisateurs
async fn get_or_create_conversation(&self, user1_id: Uuid, user2_id: Uuid) -> Result<Uuid> {
let conversation = crate::hub::direct_messages::get_or_create_dm_conversation(&self.hub, user1_id, user2_id).await?;
Ok(conversation.id)
}
/// Récupère l'ID d'un salon par son nom
async fn get_room_id_by_name(&self, room_name: &str) -> Result<Uuid> {
crate::hub::channels::get_room_id_by_name(&self.hub, room_name).await
}
}