veza/veza-chat-server/docs/CHAT_PERMISSIONS.md

329 lines
8.8 KiB
Markdown
Raw Normal View History

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 10:14:38 +00:00
# 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<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`
```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<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 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