veza/veza-chat-server/src/authentication.rs
okinrev b7955a680c P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.

Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.

Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).

Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.

Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 11:14:38 +01:00

259 lines
No EOL
7.4 KiB
Rust

//! Module d'authentification pour le serveur de chat
//!
//! Gère l'authentification des utilisateurs, les sessions et les rôles
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use std::collections::HashMap;
use crate::error::{ChatError, Result};
/// Rôles des utilisateurs dans le système de chat
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum Role {
/// Utilisateur standard
User,
/// Modérateur - peut modérer les messages et gérer les salons
Moderator,
/// Administrateur - accès complet au système
Admin,
/// Utilisateur banni - accès restreint
Banned,
}
impl Role {
/// Vérifie si le rôle a les permissions d'administrateur
pub fn is_admin(&self) -> bool {
matches!(self, Role::Admin)
}
/// Vérifie si le rôle a les permissions de modérateur ou plus
pub fn is_moderator_or_above(&self) -> bool {
matches!(self, Role::Admin | Role::Moderator)
}
/// Vérifie si l'utilisateur est banni
pub fn is_banned(&self) -> bool {
matches!(self, Role::Banned)
}
/// Vérifie si l'utilisateur peut envoyer des messages
pub fn can_send_messages(&self) -> bool {
!self.is_banned()
}
/// Vérifie si l'utilisateur peut créer des salons
pub fn can_create_rooms(&self) -> bool {
matches!(self, Role::Admin | Role::Moderator | Role::User)
}
}
/// Session utilisateur active
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserSession {
/// ID unique de l'utilisateur
pub user_id: i32,
/// Nom d'utilisateur
pub username: String,
/// Rôle de l'utilisateur
pub role: Role,
/// Timestamp de connexion
pub connected_at: DateTime<Utc>,
/// Dernière activité
pub last_activity: DateTime<Utc>,
/// Adresse IP de connexion
pub ip_address: String,
/// User agent du client
pub user_agent: Option<String>,
/// Salons auxquels l'utilisateur est connecté
pub active_rooms: Vec<String>,
/// Statut de présence
pub presence_status: PresenceStatus,
}
/// Statut de présence de l'utilisateur
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PresenceStatus {
Online,
Away,
Busy,
Offline,
}
impl UserSession {
/// Crée une nouvelle session utilisateur
pub fn new(
user_id: i32,
username: String,
role: Role,
ip_address: String,
user_agent: Option<String>,
) -> Self {
let now = Utc::now();
Self {
user_id,
username,
role,
connected_at: now,
last_activity: now,
ip_address,
user_agent,
active_rooms: Vec::new(),
presence_status: PresenceStatus::Online,
}
}
/// Met à jour la dernière activité
pub fn update_activity(&mut self) {
self.last_activity = Utc::now();
}
/// Ajoute l'utilisateur à un salon
pub fn join_room(&mut self, room_id: String) {
if !self.active_rooms.contains(&room_id) {
self.active_rooms.push(room_id);
}
self.update_activity();
}
/// Retire l'utilisateur d'un salon
pub fn leave_room(&mut self, room_id: &str) {
self.active_rooms.retain(|r| r != room_id);
self.update_activity();
}
/// Change le statut de présence
pub fn set_presence(&mut self, status: PresenceStatus) {
self.presence_status = status;
self.update_activity();
}
/// Vérifie si l'utilisateur est dans un salon spécifique
pub fn is_in_room(&self, room_id: &str) -> bool {
self.active_rooms.contains(&room_id.to_string())
}
/// Vérifie si la session est expirée (inactivité > 1 heure)
pub fn is_expired(&self) -> bool {
let now = Utc::now();
let duration = now.signed_duration_since(self.last_activity);
duration.num_hours() > 1
}
}
/// Gestionnaire d'authentification
pub struct AuthManager {
/// Sessions actives (user_id -> session)
sessions: HashMap<i32, UserSession>,
/// Connexions WebSocket (connection_id -> user_id)
connections: HashMap<String, i32>,
}
impl AuthManager {
/// Crée un nouveau gestionnaire d'authentification
pub fn new() -> Self {
Self {
sessions: HashMap::new(),
connections: HashMap::new(),
}
}
/// Authentifie un utilisateur et crée une session
pub fn authenticate_user(
&mut self,
user_id: i32,
username: String,
role: Role,
connection_id: String,
ip_address: String,
user_agent: Option<String>,
) -> Result<&UserSession> {
// Créer ou mettre à jour la session
let session = UserSession::new(user_id, username, role, ip_address, user_agent);
// Stocker la session
self.sessions.insert(user_id, session.clone());
self.connections.insert(connection_id, user_id);
// Récupérer la session insérée (ne peut pas échouer car on vient de l'insérer)
Ok(self.sessions.get(&user_id).ok_or_else(|| {
ChatError::internal_error(format!(
"Session not found after insertion for user_id: {}",
user_id
))
})?)
}
/// Récupère une session par ID utilisateur
pub fn get_session(&self, user_id: i32) -> Option<&UserSession> {
self.sessions.get(&user_id)
}
/// Récupère une session par ID de connexion
pub fn get_session_by_connection(&self, connection_id: &str) -> Option<&UserSession> {
let user_id = self.connections.get(connection_id)?;
self.sessions.get(user_id)
}
/// Met à jour l'activité d'une session
pub fn update_activity(&mut self, user_id: i32) -> Result<()> {
if let Some(session) = self.sessions.get_mut(&user_id) {
session.update_activity();
Ok(())
} else {
Err(ChatError::unauthorized("Session non trouvée"))
}
}
/// Déconnecte un utilisateur
pub fn disconnect_user(&mut self, connection_id: &str) -> Option<UserSession> {
if let Some(user_id) = self.connections.remove(connection_id) {
// Retirer de tous les salons
if let Some(mut session) = self.sessions.remove(&user_id) {
session.active_rooms.clear();
session.presence_status = PresenceStatus::Offline;
Some(session)
} else {
None
}
} else {
None
}
}
/// Nettoie les sessions expirées
pub fn cleanup_expired_sessions(&mut self) -> Vec<i32> {
let mut expired_users = Vec::new();
self.sessions.retain(|&user_id, session| {
if session.is_expired() {
expired_users.push(user_id);
false
} else {
true
}
});
// Nettoyer aussi les connexions
self.connections.retain(|_, &mut user_id| {
!expired_users.contains(&user_id)
});
expired_users
}
/// Récupère toutes les sessions actives
pub fn get_active_sessions(&self) -> Vec<&UserSession> {
self.sessions.values().collect()
}
/// Récupère le nombre de sessions actives
pub fn active_session_count(&self) -> usize {
self.sessions.len()
}
}
impl Default for AuthManager {
fn default() -> Self {
Self::new()
}
}