use dashmap::DashMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::HashMap; /// Describes the default behaviour for permissions. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionDefault { /// Permission is not granted by default. Deny, /// Permission is granted by default. Allow, /// Permission is granted by default to operators. Op(PermissionLvl), } /// Defines a permission node in the system. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Permission { /// The full node name (e.g., "minecraft:command.gamemode"). pub node: String, /// Description of what this permission does. pub description: String, /// The default value of this permission. pub default: PermissionDefault, /// Children nodes that are affected by this permission. pub children: HashMap, } impl Permission { /// Creates a new `Permission` instance. /// /// # Parameters /// - `node`: The full permission node string (e.g., `"minecraft:command.gamemode"`). /// - `description`: A human-readable description of what this permission does. /// - `default`: The default behaviour of the permission (`PermissionDefault`). /// /// # Returns /// A new `Permission` with an empty set of children. #[must_use] pub fn new(node: &str, description: &str, default: PermissionDefault) -> Self { Self { node: node.to_string(), description: description.to_string(), default, children: HashMap::new(), } } /// Adds a child permission node to this permission. /// /// # Arguments /// * `child` - Child node name. /// * `value` - Whether the child is allowed by default. /// /// # Returns /// Mutable reference to self for chaining. pub fn add_child(&mut self, child: &str, value: bool) -> &mut Self { self.children.insert(child.to_string(), value); self } } /// Repository for all registered permissions in the server. #[derive(Default)] pub struct PermissionRegistry { /// All registered permissions. permissions: DashMap, } impl PermissionRegistry { /// Creates a new empty `PermissionRegistry`. /// /// # Returns /// A `PermissionRegistry` with no permissions registered. #[must_use] pub fn new() -> Self { Self { permissions: DashMap::new(), } } /// Registers a new permission in the registry. /// /// # Parameters /// - `permission`: The `Permission` instance to add. /// /// # Returns /// - `Ok(())` if the permission was successfully registered. /// - `Err(String)` if a permission with the same node already exists. pub fn register_permission(&self, permission: Permission) -> Result<(), String> { if self.permissions.contains_key(&permission.node) { return Err(format!( "Permission {} is already registered", permission.node )); } self.permissions.insert(permission.node.clone(), permission); Ok(()) } /// Registers a new permission in the registry and expects it to be registered. /// /// # Panics /// /// Panics if the permission could not be registered (if one with the same node already exists). /// /// # Parameters /// - `permission`: The `Permission` instance to add. #[allow(clippy::expect_used)] pub fn register_permission_or_panic(&self, permission: Permission) { self.register_permission(permission) .expect("Permission should have been registered successfully"); } /// Retrieves a permission node by its name. /// /// # Parameters /// - `node`: The full permission node string to look up. /// /// # Returns /// `Some(Ref<'_, String, Permission>)` if the node exists, or `None` otherwise. #[must_use] pub fn get_permission( &self, node: &str, ) -> Option> { self.permissions.get(node) } /// Overrides the default behaviour of an already-registered permission. /// /// Used to apply per-command permission overrides from the server /// configuration after the built-in permissions have been registered. /// /// # Parameters /// - `node`: The full permission node string to update. /// - `default`: The new default behaviour to apply. /// /// # Returns /// - `true` if the node existed and its default was updated. /// - `false` if no permission with that node is registered. #[must_use] pub fn set_default(&self, node: &str, default: PermissionDefault) -> bool { if let Some(mut permission) = self.permissions.get_mut(node) { permission.default = default; true } else { false } } /// Checks whether a permission node exists in the registry. /// /// # Parameters /// - `node`: The permission node string to check. /// /// # Returns /// `true` if the node exists, `false` otherwise. #[must_use] pub fn has_permission(&self, node: &str) -> bool { self.permissions.contains_key(node) } } /// Storage for player permissions. #[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct PermissionAttachment { /// Directly assigned permissions. pub permissions: HashMap, } impl PermissionAttachment { /// Creates a new empty `PermissionAttachment`. /// /// # Returns /// A `PermissionAttachment` with no permissions set. #[must_use] pub fn new() -> Self { Self { permissions: HashMap::new(), } } /// Sets a permission value for a specific node. /// /// # Parameters /// - `node`: The permission node string. /// - `value`: Whether the permission is granted (`true`) or denied (`false`). pub fn set_permission(&mut self, node: &str, value: bool) { self.permissions.insert(node.to_string(), value); } /// Removes a permission from this attachment. /// /// # Parameters /// - `node`: The permission node string to remove. pub fn unset_permission(&mut self, node: &str) { self.permissions.remove(node); } /// Checks if a permission is explicitly set. /// /// # Parameters /// - `node`: The permission node string to query. /// /// # Returns /// `Some(true)` if granted, `Some(false)` if denied, or `None` if not set. #[must_use] pub fn has_permission_set(&self, node: &str) -> Option { self.permissions.get(node).copied() } /// Returns a reference to all set permissions. /// /// # Returns /// A `&HashMap` containing all permission nodes and their values. #[must_use] pub const fn get_permissions(&self) -> &HashMap { &self.permissions } } /// Manager for player and server permissions. #[derive(Default)] pub struct PermissionManager { /// Global registry of permissions. pub registry: PermissionRegistry, /// Player permission attachments keyed by player UUID. pub attachments: DashMap>, } impl PermissionManager { /// Creates a new empty `PermissionManager`. #[must_use] pub fn new() -> Self { Self { registry: PermissionRegistry::new(), attachments: DashMap::new(), } } /// Creates a new `PermissionManager` with an existing `PermissionRegistry`. #[must_use] pub fn with_registry(registry: PermissionRegistry) -> Self { Self { registry, attachments: DashMap::new(), } } /// Registers a new permission node in the global registry. pub fn register_permission(&self, permission: Permission) -> Result<(), String> { self.registry.register_permission(permission) } /// Registers a new permission node in the global registry or panics if already registered. pub fn register_permission_or_panic(&self, permission: Permission) { self.registry.register_permission_or_panic(permission); } /// Retrieves a permission node by its name from the registry. #[must_use] pub fn get_permission( &self, node: &str, ) -> Option> { self.registry.get_permission(node) } /// Overrides the default behaviour of an already-registered permission in the registry. #[must_use] pub fn set_default(&self, node: &str, default: PermissionDefault) -> bool { self.registry.set_default(node, default) } /// Checks whether a permission node exists in the registry. #[must_use] pub fn has_registered_permission(&self, node: &str) -> bool { self.registry.has_permission(node) } /// Sets a player's explicit permission. pub fn set_permission(&self, player_id: uuid::Uuid, node: impl Into, value: bool) { self.attachments .entry(player_id) .or_default() .insert(node.into(), value); } /// Unsets a player's explicit permission. pub fn unset_permission(&self, player_id: &uuid::Uuid, node: &str) { if let Some(player_perms) = self.attachments.get(player_id) { player_perms.remove(node); } } /// Checks if a permission is explicitly set for a player. #[must_use] pub fn has_permission_set(&self, player_id: &uuid::Uuid, node: &str) -> Option { self.attachments .get(player_id) .and_then(|p| p.get(node).as_deref().copied()) } /// Removes all permission attachments for a player. pub fn remove_attachment(&self, player_id: &uuid::Uuid) { self.attachments.remove(player_id); } /// Retrieves all explicitly assigned permissions for a player. #[must_use] pub fn get_player_permissions(&self, player_id: &uuid::Uuid) -> Option> { self.attachments.get(player_id).map(|p| { p.iter() .map(|item| (item.key().clone(), *item.value())) .collect() }) } /// Checks if a player has a specific permission. /// /// # Parameters /// - `player_id`: The UUID of the player. /// - `permission_node`: The permission node string to check (e.g., "minecraft:command.gamemode"). /// - `player_op_level`: The operator level of the player (`PermissionLvl`). /// /// # Returns /// `true` if the player has the permission, `false` otherwise. #[must_use] pub fn has_permission( &self, player_id: &uuid::Uuid, permission_node: &str, player_op_level: PermissionLvl, ) -> bool { // Check explicitly set permissions if let Some(attachment) = self.attachments.get(player_id) { // Check for the exact permission match if let Some(value) = attachment.get(permission_node) { return *value; } // Check parent nodes (for wildcard permissions) let node_parts: Vec<&str> = permission_node.split(':').collect(); if node_parts.len() == 2 { let namespace = node_parts[0]; let key_parts: Vec<&str> = node_parts[1].split('.').collect(); // Check wildcard permissions at each level let mut current_node = namespace.to_string(); if let Some(value) = attachment.get(&format!("{current_node}:*")) { return *value; } current_node.push(':'); for (i, part) in key_parts.iter().enumerate() { current_node.push_str(part); if let Some(value) = attachment.get(¤t_node) { return *value; } if i < key_parts.len() - 1 { if let Some(value) = attachment.get(&format!("{current_node}.*")) { return *value; } current_node.push('.'); } } } // Check for inherited permissions from parent nodes for item in attachment.iter() { let node = item.key(); let value = *item.value(); if let Some(permission) = self.registry.get_permission(node) && let Some(child_val) = permission.children.get(permission_node) { return value && *child_val; } } } // Fall back to the default permission value self.registry .get_permission(permission_node) .is_some_and(|permission| match permission.default { PermissionDefault::Allow => true, PermissionDefault::Deny => false, PermissionDefault::Op(required_level) => player_op_level >= required_level, }) } } /// Represents the player's permission level /// /// Permission levels determine the player's access to commands and server operations. /// Each numeric level corresponds to a specific role: /// - `Zero`: `normal`: Player can use basic commands. /// - `One`: `moderator`: Player can bypass spawn protection. /// - `Two`: `gamemaster`: Player or executor can use more commands and player can use command blocks. /// - `Three`: `admin`: Player or executor can use commands related to multiplayer management. /// - `Four`: `owner`: Player or executor can use all of the commands, including commands related to server management. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum PermissionLvl { /// Normal player. Can use basic commands. #[default] Zero = 0, /// Moderator. Can bypass spawn protection. One = 1, /// Gamemaster. Can use additional commands, including command blocks. Two = 2, /// Admin. Can manage multiplayer commands and moderate players. Three = 3, /// Owner. Full access to all commands and server management. Four = 4, } impl PartialOrd for PermissionLvl { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for PermissionLvl { fn cmp(&self, other: &Self) -> std::cmp::Ordering { (*self as u8).cmp(&(*other as u8)) } } impl Serialize for PermissionLvl { fn serialize( &self, serializer: S, ) -> Result<::Ok, ::Error> { serializer.serialize_u8(*self as u8) } } impl<'de> Deserialize<'de> for PermissionLvl { fn deserialize>(deserializer: D) -> Result { let value = u8::deserialize(deserializer)?; match value { 0 => Ok(Self::Zero), 2 => Ok(Self::Two), 3 => Ok(Self::Three), 4 => Ok(Self::Four), _ => Err(serde::de::Error::custom(format!( "Invalid value for OpLevel: {value}" ))), } } }