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.).
8.8 KiB
Système de Permissions du Chat Server
Vue d'ensemble
Le système de permissions du chat server Veza fournit un contrôle d'accès granulaire pour les conversations, avec support des rôles (admin, moderator, member) et vérifications centralisées.
Architecture
Module security/permission.rs
Le module PermissionService centralise toutes les vérifications de permissions :
pub struct PermissionService {
pool: PgPool,
}
Fonctions principales
user_in_conversation(user_id, conversation_id) -> Result<bool>
Vérifie si un utilisateur est membre d'une conversation.
Retourne : true si membre, false sinon.
user_role_in_conversation(user_id, conversation_id) -> Result<Role>
Récupère le rôle d'un utilisateur dans une conversation spécifique.
Retourne : Le rôle (Admin, Moderator, User, SuperAdmin) ou une erreur si non membre.
user_global_role(user_id) -> Result<Role>
Récupère le rôle global d'un utilisateur depuis la table users.
Retourne : Le rôle global, ou User par défaut.
can_send_message(user_id, conversation_id) -> Result<()>
Vérifie si un utilisateur peut envoyer un message dans une conversation.
Règles :
- Les membres peuvent envoyer des messages
- Les admins globaux peuvent envoyer des messages même sans être membres
- Les non-membres (non-admin) sont refusés
can_read_conversation(user_id, conversation_id) -> Result<()>
Vérifie si un utilisateur peut lire une conversation.
Règles :
- Les membres peuvent lire
- Les admins globaux peuvent lire même sans être membres
- Les non-membres (non-admin) sont refusés
can_mark_read(user_id, conversation_id) -> Result<()>
Vérifie si un utilisateur peut marquer un message comme lu.
Règles : Identiques à can_read_conversation.
can_join_conversation(user_id, conversation_id) -> Result<()>
Vérifie si un utilisateur peut rejoindre une conversation.
Règles :
- Les conversations publiques peuvent être rejointes par tous
- Les conversations privées nécessitent d'être membre ou admin global
Rôles et Permissions
Rôles disponibles
| Rôle | Description |
|---|---|
User |
Utilisateur standard |
Moderator |
Modérateur avec permissions étendues |
Admin |
Administrateur avec tous les pouvoirs |
SuperAdmin |
Super administrateur |
Matrice des permissions
| Action | User | Moderator | Admin | SuperAdmin |
|---|---|---|---|---|
| Envoyer message (membre) | ✅ | ✅ | ✅ | ✅ |
| Envoyer message (non-membre) | ❌ | ❌ | ✅ | ✅ |
| Lire conversation (membre) | ✅ | ✅ | ✅ | ✅ |
| Lire conversation (non-membre) | ❌ | ❌ | ✅ | ✅ |
| Marquer comme lu | ✅ | ✅ | ✅ | ✅ |
| Rejoindre conversation publique | ✅ | ✅ | ✅ | ✅ |
| Rejoindre conversation privée | ❌* | ❌* | ✅ | ✅ |
* Nécessite d'être membre de la conversation
Intégration dans les Handlers
WebSocket Handler (websocket/handler.rs)
Tous les handlers WebSocket vérifient les permissions avant d'exécuter les actions :
SendMessage
// Vérifier les permissions avant d'envoyer le message
state
.permission_service
.can_send_message(sender_uuid, conversation_id)
.await?;
JoinConversation
// Vérifier les permissions avant de rejoindre
state
.permission_service
.can_join_conversation(user_uuid, conversation_id)
.await?;
MarkAsRead
// Vérifier les permissions pour marquer comme lu
state
.permission_service
.can_mark_read(user_uuid, conversation_id)
.await?;
Message Handler (message_handler.rs)
Les handlers de messages vérifient également les permissions :
handle_room_message
Vérifie can_send_message avant d'envoyer un message dans un salon.
handle_direct_message
Vérifie can_send_message avant d'envoyer un message direct.
handle_room_history
Vérifie can_read_conversation via can_read_room_history.
handle_dm_history
Vérifie can_read_conversation via can_read_dm_conversation.
Schéma de Base de Données
Table conversation_members
CREATE TABLE conversation_members (
conversation_id UUID REFERENCES conversations(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
role VARCHAR(50) NOT NULL DEFAULT 'user',
joined_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
PRIMARY KEY (conversation_id, user_id)
);
Colonne role : Peut être 'user', 'moderator', 'admin', ou 'superadmin'.
Table users
CREATE TABLE users (
id UUID PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
role VARCHAR(20) DEFAULT 'user', -- Rôle global
...
);
Colonne role : Rôle global de l'utilisateur dans le système.
Gestion des Erreurs
Types d'erreurs
PermissionError::NotMember
L'utilisateur n'est pas membre de la conversation.
Code HTTP : 403 Forbidden
PermissionError::InsufficientPermissions
L'utilisateur n'a pas les permissions suffisantes pour l'action.
Code HTTP : 403 Forbidden
PermissionError::InvalidRole
Le rôle spécifié est invalide.
Code HTTP : 500 Internal Server Error
Logging
Toutes les violations de permissions sont loggées avec tracing::warn! :
warn!(
user_id = %user_id,
conversation_id = %conversation_id,
error = %e,
"Permission refusée pour l'envoi de message"
);
Messages WebSocket d'Erreur
Lorsqu'une permission est refusée, le client reçoit un message d'erreur :
{
"type": "error",
"message": "Permission refusée: Utilisateur non membre de la conversation",
"code": "permission_denied"
}
JWT Manager
Le JwtManager a été mis à jour pour récupérer les informations utilisateur depuis la base de données lors du refresh token :
// Récupérer username et role depuis la DB
let user_info: Option<(String, Option<String>)> = sqlx::query_as(
r#"
SELECT username, role FROM users
WHERE id = $1
"#,
)
.bind(user_uuid)
.fetch_optional(pool)
.await?;
Fallback : Si l'utilisateur n'est pas trouvé ou si le pool DB n'est pas disponible, utilise "user" / "user" par défaut (avec warning).
Tests
Les tests sont disponibles dans tests/test_permissions.rs :
test_can_send_message_non_member: Vérifie qu'un non-membre ne peut pas envoyertest_can_send_message_member: Vérifie qu'un membre peut envoyertest_can_send_message_admin_global: Vérifie qu'un admin global peut envoyer sans être membretest_can_read_conversation_non_member: Vérifie qu'un non-membre ne peut pas liretest_can_read_conversation_member: Vérifie qu'un membre peut liretest_user_in_conversation: Vérifie la fonctionuser_in_conversationtest_user_role_in_conversation: Vérifie la fonctionuser_role_in_conversationtest_integration_send_message_with_permissions: Test d'intégration complet
Note : Les tests nécessitent une base de données de test et sont marqués avec #[ignore].
Exemples d'utilisation
Vérifier les permissions avant d'envoyer un message
use chat_server::security::permission::PermissionService;
let permission_service = PermissionService::new(pool);
// Vérifier avant d'envoyer
permission_service
.can_send_message(user_id, conversation_id)
.await?;
// Envoyer le message...
Vérifier les permissions avant de lire
// Vérifier avant de lire
permission_service
.can_read_conversation(user_id, conversation_id)
.await?;
// Récupérer les messages...
Récupérer le rôle d'un utilisateur
// Rôle dans une conversation spécifique
let role = permission_service
.user_role_in_conversation(user_id, conversation_id)
.await?;
// Rôle global
let global_role = permission_service
.user_global_role(user_id)
.await?;
Sécurité
Bonnes pratiques
- Toujours vérifier les permissions avant d'exécuter une action
- Logger les violations pour audit et monitoring
- Ne jamais faire confiance au client : toutes les vérifications sont côté serveur
- Utiliser le service centralisé : ne pas dupliquer la logique de vérification
- Gérer les erreurs gracieusement : envoyer des messages d'erreur clairs au client
Points d'attention
- Les admins globaux peuvent contourner certaines restrictions (par design)
- Les conversations privées nécessitent une vérification explicite d'appartenance
- Le rôle dans
conversation_memberspeut différer du rôle global dansusers
Évolution future
- Support de permissions custom par conversation
- Permissions granulaires (edit, delete, pin, etc.)
- Système de rôles hiérarchiques
- Permissions temporaires (time-based)
- Audit trail des changements de permissions