214 lines
7.3 KiB
Rust
214 lines
7.3 KiB
Rust
/// # 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
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PermissionStack {
|
|
/// Permissions au niveau du serveur (None = non configuré)
|
|
pub server: Option<u64>,
|
|
|
|
/// Permissions de TOUS les groupes de l'utilisateur (None = aucun groupe)
|
|
pub groups: Option<u64>,
|
|
|
|
/// Permissions de l'utilisateur au niveau serveur (None = non défini)
|
|
pub server_user: Option<u64>,
|
|
|
|
/// Permissions de TOUS les groupes au niveau serveur (None = non défini)
|
|
pub server_groups: Option<u64>,
|
|
|
|
/// Permissions de l'utilisateur sur ce canal (None = non défini)
|
|
pub channel_user: Option<u64>,
|
|
|
|
/// Permissions de TOUS les groupes sur ce canal (None = non défini)
|
|
pub channel_groups: Option<u64>,
|
|
}
|
|
|
|
impl PermissionStack {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
server: None,
|
|
groups: None,
|
|
server_user: None,
|
|
server_groups: None,
|
|
channel_user: None,
|
|
channel_groups: None,
|
|
}
|
|
}
|
|
|
|
/// Résout les permissions finales en mode Union
|
|
/// On accumule TOUTES les sources définies avec OR
|
|
pub fn resolve(&self) -> u64 {
|
|
let mut result = 0u64;
|
|
|
|
if let Some(perms) = self.server {
|
|
result |= perms;
|
|
}
|
|
if let Some(perms) = self.groups {
|
|
result |= perms;
|
|
}
|
|
if let Some(perms) = self.server_user {
|
|
result |= perms;
|
|
}
|
|
if let Some(perms) = self.server_groups {
|
|
result |= perms;
|
|
}
|
|
if let Some(perms) = self.channel_user {
|
|
result |= perms;
|
|
}
|
|
if let Some(perms) = self.channel_groups {
|
|
result |= perms;
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/// Vérifie si une permission spécifique est accordée.
|
|
///
|
|
/// ### Exemple :
|
|
/// ```rust
|
|
/// if stack.has(Permission::SendMessage) {
|
|
/// println!("L'utilisateur peut envoyer des messages !");
|
|
/// }
|
|
/// ```
|
|
pub fn has(&self, permission: Permission) -> bool {
|
|
let mask = self.resolve();
|
|
mask & (permission as u64) != 0
|
|
}
|
|
|
|
/// Vérifie si TOUTES les permissions listées sont accordées (Strict).
|
|
///
|
|
/// Utile pour des actions nécessitant plusieurs droits combinés.
|
|
///
|
|
/// ### Exemple :
|
|
/// ```rust
|
|
/// // L'utilisateur doit pouvoir voir ET parler pour rejoindre un vocal
|
|
/// let perms = [Permission::ReadChannel, Permission::VoiceSpeak];
|
|
/// if stack.has_all(&perms) {
|
|
/// join_voice_channel();
|
|
/// }
|
|
/// ```
|
|
pub fn has_all(&self, permissions: &[Permission]) -> bool {
|
|
let required = permissions.iter().fold(0u64, |acc, &p| acc | p as u64);
|
|
let mask = self.resolve();
|
|
(mask & required) == required
|
|
}
|
|
|
|
/// Vérifie si AU MOINS UNE des permissions listées est accordée.
|
|
///
|
|
/// Utile pour les rôles de modération ou les accès "ou" (OR).
|
|
///
|
|
/// ### Exemple :
|
|
/// ```rust
|
|
/// // L'utilisateur peut supprimer le message s'il est modérateur OU admin
|
|
/// let moderator_perms = [Permission::DeleteOthersMessage, Permission::ManageChannel];
|
|
/// if stack.has_any(&moderator_perms) {
|
|
/// delete_message();
|
|
/// }
|
|
/// ```
|
|
pub fn has_any(&self, permissions: &[Permission]) -> bool {
|
|
let required = permissions.iter().fold(0u64, |acc, &p| acc | p as u64);
|
|
let mask = self.resolve();
|
|
(mask & required) != 0
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_permission_stack() {
|
|
let mut stack = PermissionStack::new();
|
|
|
|
// 1. Test des permissions par défaut du serveur
|
|
stack.server = Some(Permission::ReadChannel as u64 | Permission::SendMessage as u64);
|
|
assert!(stack.has(Permission::ReadChannel));
|
|
assert!(stack.has(Permission::SendMessage));
|
|
assert!(!stack.has(Permission::VoiceSpeak));
|
|
|
|
// 2. Test de l'union avec les groupes
|
|
stack.groups = Some(Permission::VoiceSpeak as u64);
|
|
assert!(stack.has(Permission::ReadChannel));
|
|
assert!(stack.has(Permission::SendMessage));
|
|
assert!(stack.has(Permission::VoiceSpeak));
|
|
|
|
// 3. Test du bypass administrateur (Saturation du masque)
|
|
stack.server_user = Some(u64::MAX);
|
|
assert!(stack.has(Permission::ManageServer));
|
|
assert!(stack.has(Permission::ManageRoles));
|
|
assert!(stack.has(Permission::KickMember));
|
|
|
|
// 4. Test spécifique au canal (Sans bypass admin)
|
|
stack.server_user = None;
|
|
stack.channel_user = Some(Permission::ManageChannel as u64);
|
|
assert!(stack.has(Permission::ReadChannel)); // Vient du serveur
|
|
assert!(stack.has(Permission::ManageChannel)); // Vient du channel_user
|
|
assert!(!stack.has(Permission::ManageRoles)); // Pas défini
|
|
}
|
|
}
|