Mountain/IPC/Security/
Permission.rs

1//! # Permission Definition (IPC Security)
2//!
3//! ## RESPONSIBILITIES
4//! This module defines the Permission structure used for role-based access
5//! control (RBAC) in the IPC layer.
6//!
7//! ## ARCHITECTURAL ROLE
8//! This module provides the permission definition that represents individual
9//! access rights that can be granted to roles.
10//!
11//! ## KEY COMPONENTS
12//!
13//! - **Permission**: Permission definition with name, description, and category
14//!
15//! ## ERROR HANDLING
16//! N/A - This is a data definition module.
17//!
18//! ## LOGGING
19//! N/A - Permission creation is logged by PermissionManager.
20//!
21//! ## PERFORMANCE CONSIDERATIONS
22//! - Permission definitions are stored in HashMap for O(1) lookup
23//! - Minimal memory footprint for efficient storage
24//!
25//! ## TODO
26//! - Add permission metadata (creation time, last used)
27//! - Implement permission aliases
28//! - Support permission hierarchies (e.g., "file.*" includes all file
29//!   permissions)
30
31use serde::{Deserialize, Serialize};
32
33/// Permission definition for RBAC
34///
35/// Permissions represent individual access rights that can be granted to roles.
36/// They follow a naming convention of "resource.action" (e.g., "file.read",
37/// "config.update") for clear organization.
38///
39/// ## Permission Categories
40///
41/// - **file**: File system operations (read, write, delete)
42/// - **config**: Configuration management (read, update)
43/// - **storage**: Storage operations (read, write)
44/// - **system**: System-level operations (external access)
45///
46/// ## Example Usage
47///
48/// ```rust,ignore
49/// let permission = Permission {
50///     name: "file.write".to_string(),
51///     description: "Write file operations".to_string(),
52///     category: "file".to_string(),
53/// };
54/// ```
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct Permission {
57	/// Unique permission identifier (e.g., "file.read", "config.update")
58	pub name:String,
59
60	/// Human-readable description of what this permission allows
61	pub description:String,
62
63	/// Category for groupings (e.g., "file", "config", "storage")
64	pub category:String,
65}
66
67impl Permission {
68	/// Create a new permission
69	pub fn new(name:String, description:String, category:String) -> Self { Self { name, description, category } }
70
71	/// Check if this permission belongs to a specific category
72	pub fn is_in_category(&self, category:&str) -> bool { self.category == category }
73
74	/// Get the resource part of the permission name (before the dot)
75	pub fn resource(&self) -> Option<&str> { self.name.split('.').next() }
76
77	/// Get the action part of the permission name (after the dot)
78	pub fn action(&self) -> Option<&str> { self.name.split('.').nth(1) }
79}
80
81#[cfg(test)]
82mod tests {
83	use super::*;
84
85	#[test]
86	fn test_permission_creation() {
87		let permission =
88			Permission::new("file.read".to_string(), "Read file operations".to_string(), "file".to_string());
89
90		assert_eq!(permission.name, "file.read");
91		assert_eq!(permission.description, "Read file operations");
92		assert_eq!(permission.category, "file");
93	}
94
95	#[test]
96	fn test_is_in_category() {
97		let permission =
98			Permission::new("file.read".to_string(), "Read file operations".to_string(), "file".to_string());
99
100		assert!(permission.is_in_category("file"));
101		assert!(!permission.is_in_category("config"));
102	}
103
104	#[test]
105	fn test_resource() {
106		let permission =
107			Permission::new("file.read".to_string(), "Read file operations".to_string(), "file".to_string());
108
109		assert_eq!(permission.resource(), Some("file"));
110	}
111
112	#[test]
113	fn test_action() {
114		let permission =
115			Permission::new("file.read".to_string(), "Read file operations".to_string(), "file".to_string());
116
117		assert_eq!(permission.action(), Some("read"));
118	}
119
120	#[test]
121	fn test_invalid_permission_name() {
122		let permission = Permission::new("invalid".to_string(), "Invalid permission".to_string(), "test".to_string());
123
124		// Should return None for invalid format
125		assert_eq!(permission.resource(), Some("invalid"));
126		assert_eq!(permission.action(), None);
127	}
128}