# 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 : ```rust pub struct PermissionService { pool: PgPool, } ``` ### Fonctions principales #### `user_in_conversation(user_id, conversation_id) -> Result` 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` 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` 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` ```rust // Vérifier les permissions avant d'envoyer le message state .permission_service .can_send_message(sender_uuid, conversation_id) .await?; ``` #### `JoinConversation` ```rust // Vérifier les permissions avant de rejoindre state .permission_service .can_join_conversation(user_uuid, conversation_id) .await?; ``` #### `MarkAsRead` ```rust // 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` ```sql 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` ```sql 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!` : ```rust 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 : ```json { "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 : ```rust // Récupérer username et role depuis la DB let user_info: Option<(String, Option)> = 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 envoyer - `test_can_send_message_member` : Vérifie qu'un membre peut envoyer - `test_can_send_message_admin_global` : Vérifie qu'un admin global peut envoyer sans être membre - `test_can_read_conversation_non_member` : Vérifie qu'un non-membre ne peut pas lire - `test_can_read_conversation_member` : Vérifie qu'un membre peut lire - `test_user_in_conversation` : Vérifie la fonction `user_in_conversation` - `test_user_role_in_conversation` : Vérifie la fonction `user_role_in_conversation` - `test_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 ```rust 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 ```rust // 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 ```rust // 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 1. **Toujours vérifier les permissions** avant d'exécuter une action 2. **Logger les violations** pour audit et monitoring 3. **Ne jamais faire confiance au client** : toutes les vérifications sont côté serveur 4. **Utiliser le service centralisé** : ne pas dupliquer la logique de vérification 5. **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_members` peut différer du rôle global dans `users` ## É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