Init
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
pub mod auth;
|
||||
pub mod password;
|
||||
pub mod permissions;
|
||||
pub mod ssh_auth;
|
||||
pub mod toolbox;
|
||||
|
||||
183
src/utils/permissions.rs
Normal file
183
src/utils/permissions.rs
Normal file
@@ -0,0 +1,183 @@
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// # Système de Permissions par Bitmask
|
||||
///
|
||||
/// Un bitmask (masque de bits) est une manière compacte et efficace de stocker plusieurs
|
||||
/// valeurs booléennes dans un seul nombre entier.
|
||||
///
|
||||
/// ## Comment ça fonctionne ?
|
||||
/// Chaque permission est associée à une puissance de 2 (1, 2, 4, 8, 16, etc.), ce qui
|
||||
/// correspond à un bit unique dans la représentation binaire du nombre.
|
||||
///
|
||||
/// - `ReadChannel` (1 << 0) = `00000001` (1 en décimal)
|
||||
/// - `JoinChannel` (1 << 1) = `00000010` (2 en décimal)
|
||||
/// - `SendMessage` (1 << 2) = `00000100` (4 en décimal)
|
||||
///
|
||||
/// ## Opérations courantes :
|
||||
///
|
||||
/// ### 1. Combiner des permissions (OU binaire `|`)
|
||||
/// Pour donner à la fois `ReadChannel` et `SendMessage` :
|
||||
/// `let mask = Permission::ReadChannel as u64 | Permission::SendMessage as u64;`
|
||||
/// Résultat : `00000101` (5 en décimal)
|
||||
///
|
||||
/// ### 2. Vérifier une permission (ET binaire `&`)
|
||||
/// Pour savoir si un utilisateur a `SendMessage` dans son `mask` :
|
||||
/// `(mask & Permission::SendMessage as u64) != 0`
|
||||
///
|
||||
/// ### 3. Retirer une permission (NON `!`, ET `&`)
|
||||
/// `mask &= !(Permission::SendMessage as u64);`
|
||||
///
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u64)]
|
||||
pub enum Permission {
|
||||
/// Pouvoir voir le canal dans la liste et lire les messages
|
||||
ReadChannel = 1 << 0,
|
||||
/// Pouvoir rejoindre le canal vocal
|
||||
JoinChannel = 1 << 1,
|
||||
/// Pouvoir envoyer des messages
|
||||
SendMessage = 1 << 2,
|
||||
/// Pouvoir supprimer ses propres messages
|
||||
DeleteMessage = 1 << 3,
|
||||
/// Pouvoir supprimer les messages des autres (Modérateur)
|
||||
DeleteOthersMessage = 1 << 4,
|
||||
/// Pouvoir modifier les paramètres du canal
|
||||
ManageChannel = 1 << 5,
|
||||
/// Pouvoir gérer les groupes/permissions du serveur
|
||||
ManageRoles = 1 << 6,
|
||||
/// Pouvoir expulser des membres du serveur
|
||||
KickMember = 1 << 7,
|
||||
/// Pouvoir parler en vocal
|
||||
VoiceSpeak = 1 << 8,
|
||||
/// Pouvoir rendre muet les autres utilisateurs en vocal
|
||||
VoiceMuteOthers = 1 << 9,
|
||||
/// Pouvoir modifier les paramètres globaux du serveur
|
||||
ManageServer = 1 << 10,
|
||||
}
|
||||
|
||||
impl Permission {
|
||||
/// Retourne un bitmask contenant toutes les permissions "standard"
|
||||
/// (Tout sauf les permissions de gestion "Manage...").
|
||||
pub fn default_permissions() -> u64 {
|
||||
Permission::ReadChannel as u64
|
||||
| Permission::JoinChannel as u64
|
||||
| Permission::SendMessage as u64
|
||||
| Permission::DeleteMessage as u64
|
||||
| Permission::DeleteOthersMessage as u64
|
||||
| Permission::KickMember as u64
|
||||
| Permission::VoiceSpeak as u64
|
||||
| Permission::VoiceMuteOthers as u64
|
||||
}
|
||||
}
|
||||
|
||||
/// Contexte de permissions calculé pour un utilisateur donné.
|
||||
///
|
||||
/// Ce contexte regroupe les permissions globales (issues de la fusion de tous les groupes
|
||||
/// de l'utilisateur) et les surcharges spécifiques par canal (overrides).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PermissionContext {
|
||||
/// Union des masques de tous les groupes auxquels appartient l'utilisateur.
|
||||
pub global: u64,
|
||||
/// Masques spécifiques par canal (si présents, ils remplacent totalement le masque global
|
||||
/// pour ce canal spécifique).
|
||||
pub channel_overrides: HashMap<Uuid, u64>,
|
||||
}
|
||||
|
||||
impl PermissionContext {
|
||||
/// Crée un nouveau contexte à partir des masques bruts de la base de données.
|
||||
///
|
||||
/// - `group_masks` : Liste des permissions de chaque groupe de l'utilisateur.
|
||||
/// - `channel_permissions` : Liste de (ID Canal, Masque) pour les permissions spécifiques.
|
||||
pub fn new(group_masks: &[i64], channel_permissions: Vec<(Uuid, i64)>) -> Self {
|
||||
// On combine tous les groupes avec l'opérateur OR (|)
|
||||
let global = group_masks.iter().fold(0u64, |acc, &m| acc | m as u64);
|
||||
|
||||
let channel_overrides = channel_permissions
|
||||
.into_iter()
|
||||
.map(|(id, m)| (id, m as u64))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
global,
|
||||
channel_overrides,
|
||||
}
|
||||
}
|
||||
|
||||
/// Vérifie si une permission spécifique est accordée, optionnellement pour un canal précis.
|
||||
///
|
||||
/// Si `channel_id` est fourni et qu'une surcharge existe pour ce canal, la surcharge
|
||||
/// est utilisée. Sinon, on utilise les permissions globales du serveur.
|
||||
///
|
||||
/// ### Exemple d'utilisation :
|
||||
/// ```rust
|
||||
/// if ctx.has(Permission::SendMessage, Some(channel_id)) {
|
||||
/// // Autorisé !
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn has(&self, permission: Permission, channel_id: Option<Uuid>) -> bool {
|
||||
let mask = if let Some(cid) = channel_id {
|
||||
self.channel_overrides
|
||||
.get(&cid)
|
||||
.copied()
|
||||
.unwrap_or(self.global)
|
||||
} else {
|
||||
self.global
|
||||
};
|
||||
|
||||
mask & (permission as u64) != 0
|
||||
}
|
||||
|
||||
/// Méthode utilitaire pour vérifier rapidement l'accès en lecture à un canal.
|
||||
pub fn can_see_channel(&self, channel_id: Uuid) -> bool {
|
||||
self.has(Permission::ReadChannel, Some(channel_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_permission_context() {
|
||||
let channel_id = Uuid::new_v4();
|
||||
let other_channel_id = Uuid::new_v4();
|
||||
|
||||
// On imagine un utilisateur avec 2 groupes
|
||||
let role_masks = vec![
|
||||
Permission::ReadChannel as i64 | Permission::SendMessage as i64, // Groupe 1 : lecture + envoi
|
||||
Permission::VoiceSpeak as i64, // Groupe 2 : vocal
|
||||
];
|
||||
|
||||
// Et une permission spécifique sur un canal (Lecture + Envoi + Gestion)
|
||||
let channel_permissions = vec![(
|
||||
channel_id,
|
||||
Permission::ReadChannel as i64
|
||||
| Permission::SendMessage as i64
|
||||
| Permission::ManageChannel as i64,
|
||||
)];
|
||||
|
||||
let ctx = PermissionContext::new(&role_masks, channel_permissions);
|
||||
|
||||
// Test des permissions globales
|
||||
assert!(ctx.has(Permission::ReadChannel, None));
|
||||
assert!(ctx.has(Permission::SendMessage, None));
|
||||
assert!(ctx.has(Permission::VoiceSpeak, None));
|
||||
assert!(!ctx.has(Permission::ManageChannel, None));
|
||||
|
||||
// Test des surcharges par canal (L'override remplace le masque global)
|
||||
assert!(ctx.has(Permission::ReadChannel, Some(channel_id)));
|
||||
assert!(ctx.has(Permission::SendMessage, Some(channel_id)));
|
||||
assert!(ctx.has(Permission::ManageChannel, Some(channel_id)));
|
||||
assert!(!ctx.has(Permission::VoiceSpeak, Some(channel_id))); // Ici on perd le vocal car l'override remplace tout
|
||||
|
||||
// Test d'un autre canal (pas de surcharge, on retombe sur le global)
|
||||
assert!(ctx.has(Permission::ReadChannel, Some(other_channel_id)));
|
||||
assert!(ctx.has(Permission::VoiceSpeak, Some(other_channel_id)));
|
||||
assert!(!ctx.has(Permission::ManageChannel, Some(other_channel_id)));
|
||||
|
||||
// Test des fonctions sucre
|
||||
assert!(ctx.can_see_channel(channel_id));
|
||||
assert!(ctx.can_see_channel(other_channel_id));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user