Mountain/IPC/Permission/Role/
ManageRole.rs

1//! # Manage Role
2//!
3//! ## File: IPC/Permission/Role/ManageRole.rs
4//!
5//! ## Role in Mountain Architecture
6//!
7//! Defines and manages role structures for role-based access control (RBAC),
8//! providing organizational hierarchy for user permissions across the system.
9//!
10//! ## Primary Responsibility
11//!
12//! Define role and permission structures for RBAC system with inheritance
13//! support.
14//!
15//! ## Secondary Responsibilities
16//!
17//! - Create role definitions with assigned permissions
18//! - Create permission definitions with categorization
19//! - Support role hierarchy and permission inheritance
20//! - Validate role and permission integrity
21//!
22//! ## Dependencies
23//!
24//! **External Crates:**
25//! - `serde` - Serialization for storage and transport
26//! - `std::collections::HashSet` - Unique permission tracking
27//!
28//! **Internal Modules:**
29//! - `Validate::{SecurityContext}` - Security context validation
30//! - `LogEvent::{SecurityEvent}` - Security event logging
31//!
32//! ## Dependents
33//!
34//! - `Validate` - Uses roles for permission validation
35//! - `TauriIPCServer` - Manages roles for IPC authorization
36//!
37//! ## VSCode Pattern Reference
38//!
39//! Matches VSCode's role system in
40//! `vs/platform/permissions/common/permissions.ts`
41//! - Hierarchical role definitions
42//! - Permission categorization
43//! - Role inheritance support
44//! - Permission uniqueness validation
45//!
46//! ## Security Considerations
47//!
48//! - Role names are case-sensitive for precise control
49//! - Permission names follow hierarchical naming (category.action)
50//! - Role inheritance prevents permission escalation through ambiguity
51//! - Role modifications logged for audit trails
52//! - Default roles cannot be deleted without confirmation
53//! - Permission deduplication prevents duplicate permissions in roles
54//!
55//! ## Performance Considerations
56//!
57//! - HashSet for unique permissions enables O(1) lookup
58//! - Role hierarchy flattened for fast permission resolution
59//! - Lazy initialization of role collections
60//! - Minimal copying of permission data
61//!
62//! ## Error Handling Strategy
63//!
64//! - Returns Result for explicit error handling
65//! - Duplicate permissions ignored with warning
66//! - Invalid role/permission names rejected early
67//! - Circular dependency detection in role hierarchy
68//!
69//! ## Thread Safety
70//!
71//! - Immutable role definitions after creation
72//! - Clone semantics for safe sharing across threads
73//!
74//! ## TODO Items
75//!
76//! - [ ] Implement role hierarchy with parent/child relationships
77//! - [ ] Add permission negation (deny permissions)
78//! - [ ] Support role templates for common permission sets
79
80use std::collections::HashSet;
81
82use serde::{Deserialize, Serialize};
83use log::{debug, info, warn};
84
85/// Role definition for RBAC system
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct Role {
88	/// Unique role identifier (case-sensitive)
89	pub Name:String,
90
91	/// Permissions granted by this role (unique, deduplicated)
92	pub Permissions:Vec<String>,
93
94	/// Human-readable description of role purpose
95	pub Description:String,
96
97	/// Optional parent role for inheritance (not yet implemented)
98	pub ParentRole:Option<String>,
99
100	/// Role priority for conflict resolution (higher = more important)
101	pub Priority:u32,
102}
103
104/// Permission definition
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Permission {
107	/// Unique permission identifier (formatted as category.action)
108	pub Name:String,
109
110	/// Human-readable description of permission purpose
111	pub Description:String,
112
113	/// Permission category for organization
114	pub Category:String,
115
116	/// Whether this permission is sensitive (requires special logging)
117	pub IsSensitive:bool,
118}
119
120impl Role {
121	/// Create a new role definition
122	///
123	/// ## Parameters
124	/// - `Name`: Unique role identifier
125	/// - `Permissions`: List of permission strings
126	/// - `Description`: Human-readable description
127	///
128	/// ## Returns
129	/// New Role instance with deduplicated permissions
130	///
131	/// ## Notes
132	/// - Permissions are automatically deduplicated
133	/// - Default priority is 0
134	pub fn New(Name:String, Permissions:Vec<String>, Description:String) -> Self {
135		let UniquePermissions:Vec<String> = Permissions.into_iter().collect::<HashSet<String>>().into_iter().collect();
136
137		Self { Name, Permissions:UniquePermissions, Description, ParentRole:None, Priority:0 }
138	}
139
140	/// Create a new role with parent inheritance
141	///
142	/// ## Parameters
143	/// - `Name`: Unique role identifier
144	/// - `Permissions`: List of permission strings
145	/// - `Description`: Human-readable description
146	/// - `ParentRole`: Name of parent role to inherit from
147	/// - `Priority`: Role priority level
148	///
149	/// ## Returns
150	/// New Role instance with inheritance configuration
151	pub fn NewWithParent(
152		Name:String,
153		Permissions:Vec<String>,
154		Description:String,
155		ParentRole:String,
156		Priority:u32,
157	) -> Self {
158		let UniquePermissions:Vec<String> = Permissions.into_iter().collect::<HashSet<String>>().into_iter().collect();
159
160		Self {
161			Name,
162			Permissions:UniquePermissions,
163			Description,
164			ParentRole:Some(ParentRole),
165			Priority,
166		}
167	}
168
169	/// Add a permission to this role
170	///
171	/// ## Parameters
172	/// - `Permission`: Permission string to add
173	///
174	/// ## Returns
175	/// Self for method chaining
176	pub fn AddPermission(mut self, Permission:String) -> Self {
177		if !self.Permissions.contains(&Permission) {
178			self.Permissions.push(Permission.clone());
179			debug!("[Role] Added permission '{}' to role '{}'", Permission, self.Name);
180		} else {
181			debug!(
182				"[Role] Permission '{}' already exists in role '{}', skipping",
183				Permission, self.Name
184			);
185		}
186		self
187	}
188
189	/// Add multiple permissions to this role
190	///
191	/// ## Parameters
192	/// - `Permissions`: Iterator of permission strings to add
193	///
194	/// ## Returns
195	/// Self for method chaining
196	pub fn AddPermissions(mut self, Permissions:impl IntoIterator<Item = String>) -> Self {
197		for Permission in Permissions {
198			if !self.Permissions.contains(&Permission) {
199				self.Permissions.push(Permission.clone());
200				debug!("[Role] Added permission '{}' to role '{}'", Permission, self.Name);
201			}
202		}
203		self
204	}
205
206	/// Check if this role has a specific permission
207	///
208	/// ## Parameters
209	/// - `Permission`: Permission string to check
210	///
211	/// ## Returns
212	/// true if role has permission, false otherwise
213	pub fn HasPermission(&self, Permission:&str) -> bool { self.Permissions.contains(&Permission.to_string()) }
214
215	/// Get the count of permissions in this role
216	///
217	/// ## Returns
218	/// Number of unique permissions
219	pub fn PermissionCount(&self) -> usize { self.Permissions.len() }
220
221	/// Validate role structure integrity
222	///
223	/// ## Returns
224	/// Result indicating success or validation error
225	pub fn Validate(&self) -> Result<(), String> {
226		if self.Name.is_empty() {
227			return Err("Role name cannot be empty".to_string());
228		}
229
230		if self.Name.contains(|c:char| c.is_whitespace()) {
231			return Err("Role name cannot contain whitespace".to_string());
232		}
233
234		if self.Description.is_empty() {
235			return Err("Role description cannot be empty".to_string());
236		}
237
238		// Validate permission names
239		for Permission in &self.Permissions {
240			if Permission.is_empty() {
241				return Err("Permission name cannot be empty".to_string());
242			}
243
244			if !Permission.contains('.') {
245				return Err(format!(
246					"Permission '{}' must contain a dot separating category and action",
247					Permission
248				));
249			}
250
251			if Permission.contains(|c:char| c.is_whitespace()) {
252				return Err(format!("Permission '{}' cannot contain whitespace", Permission));
253			}
254		}
255
256		Ok(())
257	}
258}
259
260impl Permission {
261	/// Create a new permission definition
262	///
263	/// ## Parameters
264	/// - `Name`: Unique permission identifier (category.action format)
265	/// - `Description`: Human-readable description
266	/// - `Category`: Permission category
267	///
268	/// ## Returns
269	/// New Permission instance
270	pub fn New(Name:String, Description:String, Category:String) -> Self {
271		Self { Name, Description, Category, IsSensitive:false }
272	}
273
274	/// Create a new sensitive permission (requires special logging)
275	///
276	/// ## Parameters
277	/// - `Name`: Unique permission identifier
278	/// - `Description`: Human-readable description
279	/// - `Category`: Permission category
280	///
281	/// ## Returns
282	/// New Permission instance marked as sensitive
283	pub fn NewSensitive(Name:String, Description:String, Category:String) -> Self {
284		Self { Name, Description, Category, IsSensitive:true }
285	}
286
287	/// Mark permission as sensitive
288	///
289	/// ## Returns
290	/// Self for method chaining
291	pub fn SetSensitive(mut self) -> Self {
292		self.IsSensitive = true;
293		self
294	}
295
296	/// Get the action part of the permission name (after last dot)
297	///
298	/// ## Returns
299	/// Action string or "unknown" if format is invalid
300	pub fn GetAction(&self) -> String { self.Name.rsplit('.').next().unwrap_or("unknown").to_string() }
301
302	/// Get the category part of the permission name (before last dot)
303	///
304	/// ## Returns
305	/// Category string or "unknown" if format is invalid
306	pub fn GetCategory(&self) -> String {
307		if let Some(pos) = self.Name.rfind('.') {
308			self.Name[..pos].to_string()
309		} else {
310			"unknown".to_string()
311		}
312	}
313
314	/// Validate permission structure integrity
315	///
316	/// ## Returns
317	/// Result indicating success or validation error
318	pub fn Validate(&self) -> Result<(), String> {
319		if self.Name.is_empty() {
320			return Err("Permission name cannot be empty".to_string());
321		}
322
323		if self.Name.contains(|c:char| c.is_whitespace()) {
324			return Err("Permission name cannot contain whitespace".to_string());
325		}
326
327		if !self.Name.contains('.') {
328			return Err("Permission name must contain a dot separating category and action".to_string());
329		}
330
331		if self.Description.is_empty() {
332			return Err("Permission description cannot be empty".to_string());
333		}
334
335		if self.Category.is_empty() {
336			return Err("Permission category cannot be empty".to_string());
337		}
338
339		Ok(())
340	}
341}
342
343/// Create standard user role
344///
345/// ## Returns
346/// Role configured with read-only permissions
347pub fn CreateUserRole() -> Role {
348	Role::New(
349		"user".to_string(),
350		vec!["file.read".to_string(), "config.read".to_string(), "storage.read".to_string()],
351		"Standard user with read access".to_string(),
352	)
353}
354
355/// Create developer role
356///
357/// ## Returns
358/// Role configured with read/write permissions
359pub fn CreateDeveloperRole() -> Role {
360	Role::New(
361		"developer".to_string(),
362		vec![
363			"file.read".to_string(),
364			"file.write".to_string(),
365			"config.read".to_string(),
366			"storage.read".to_string(),
367			"storage.write".to_string(),
368		],
369		"Developer with read/write access".to_string(),
370	)
371}
372
373/// Create administrator role
374///
375/// ## Returns
376/// Role configured with full system access
377pub fn CreateAdminRole() -> Role {
378	Role::New(
379		"admin".to_string(),
380		vec![
381			"file.read".to_string(),
382			"file.write".to_string(),
383			"config.read".to_string(),
384			"config.update".to_string(),
385			"storage.read".to_string(),
386			"storage.write".to_string(),
387			"system.external".to_string(),
388			"system.execute".to_string(),
389			"admin.manage".to_string(),
390		],
391		"Administrator with full access".to_string(),
392	)
393	.AddPermission("role.manage".to_string())
394}
395
396/// Create all standard roles
397///
398/// ## Returns
399/// Vector containing user, developer, and admin roles
400pub fn CreateStandardRoles() -> Vec<Role> {
401	info!("[ManageRole] Creating standard roles");
402	vec![CreateUserRole(), CreateDeveloperRole(), CreateAdminRole()]
403}
404
405/// Create all standard permissions
406///
407/// ## Returns
408/// Vector containing standard permission definitions
409pub fn CreateStandardPermissions() -> Vec<Permission> {
410	info!("[ManageRole] Creating standard permissions");
411	vec![
412		// File permissions
413		Permission::New("file.read".to_string(), "Read file operations".to_string(), "file".to_string()),
414		Permission::New(
415			"file.write".to_string(),
416			"Write file operations".to_string(),
417			"file".to_string(),
418		),
419		Permission::New(
420			"file.delete".to_string(),
421			"Delete file operations".to_string(),
422			"file".to_string(),
423		),
424		// Config permissions
425		Permission::New(
426			"config.read".to_string(),
427			"Read configuration".to_string(),
428			"config".to_string(),
429		),
430		Permission::NewSensitive(
431			"config.update".to_string(),
432			"Update configuration".to_string(),
433			"config".to_string(),
434		)
435		.SetSensitive(),
436		// Storage permissions
437		Permission::New("storage.read".to_string(), "Read storage".to_string(), "storage".to_string()),
438		Permission::New("storage.write".to_string(), "Write storage".to_string(), "storage".to_string()),
439		Permission::New(
440			"storage.delete".to_string(),
441			"Delete from storage".to_string(),
442			"storage".to_string(),
443		),
444		// System permissions
445		Permission::NewSensitive(
446			"system.external".to_string(),
447			"Access external system resources".to_string(),
448			"system".to_string(),
449		)
450		.SetSensitive(),
451		Permission::NewSensitive(
452			"system.execute".to_string(),
453			"Execute system commands".to_string(),
454			"system".to_string(),
455		)
456		.SetSensitive(),
457		// Admin permissions
458		Permission::NewSensitive(
459			"admin.manage".to_string(),
460			"Administrative management operations".to_string(),
461			"admin".to_string(),
462		)
463		.SetSensitive(),
464		Permission::NewSensitive(
465			"role.manage".to_string(),
466			"Manage roles and permissions".to_string(),
467			"admin".to_string(),
468		)
469		.SetSensitive(),
470	]
471}
472
473#[cfg(test)]
474mod Tests {
475	use super::*;
476
477	#[test]
478	fn TestCreateRole() {
479		let role = Role::New(
480			"test.role".to_string(),
481			vec!["perm1".to_string(), "perm2".to_string(), "perm1".to_string()],
482			"Test role".to_string(),
483		);
484
485		assert_eq!(role.Name, "test.role");
486		assert_eq!(role.Description, "Test role");
487		assert_eq!(role.PermissionCount(), 2, "Should deduplicate permissions");
488	}
489
490	#[test]
491	fn TestRoleHasPermission() {
492		let role = Role::New(
493			"test.role".to_string(),
494			vec!["perm1".to_string(), "perm2".to_string()],
495			"Test role".to_string(),
496		);
497
498		assert!(role.HasPermission("perm1"));
499		assert!(role.HasPermission("perm2"));
500		assert!(!role.HasPermission("perm3"));
501	}
502
503	#[test]
504	fn TestAddPermission() {
505		let role = Role::New("test.role".to_string(), vec!["perm1".to_string()], "Test role".to_string())
506			.AddPermission("perm2".to_string());
507
508		assert!(role.HasPermission("perm1"));
509		assert!(role.HasPermission("perm2"));
510	}
511
512	#[test]
513	fn TestAddPermissions() {
514		let role = Role::New("test.role".to_string(), vec!["perm1".to_string()], "Test role".to_string())
515			.AddPermissions(vec!["perm2".to_string(), "perm3".to_string()]);
516
517		assert_eq!(role.PermissionCount(), 3);
518	}
519
520	#[test]
521	fn TestRoleValidateSuccess() {
522		let role = Role::New(
523			"test.role".to_string(),
524			vec!["category.action".to_string()],
525			"Valid role".to_string(),
526		);
527
528		assert!(role.Validate().is_ok());
529	}
530
531	#[test]
532	fn TestRoleValidateEmptyName() {
533		let role = Role::New("".to_string(), vec!["category.action".to_string()], "Valid role".to_string());
534
535		assert!(role.Validate().is_err());
536	}
537
538	#[test]
539	fn TestRoleValidateWhitespaceInName() {
540		let role = Role::New(
541			"test role".to_string(),
542			vec!["category.action".to_string()],
543			"Valid role".to_string(),
544		);
545
546		assert!(role.Validate().is_err());
547	}
548
549	#[test]
550	fn TestRoleValidateEmptyDescription() {
551		let role = Role::New("test.role".to_string(), vec!["category.action".to_string()], "".to_string());
552
553		assert!(role.Validate().is_err());
554	}
555
556	#[test]
557	fn TestPermissionNew() {
558		let perm = Permission::New("file.read".to_string(), "Read files".to_string(), "file".to_string());
559
560		assert_eq!(perm.Name, "file.read");
561		assert_eq!(perm.Description, "Read files");
562		assert_eq!(perm.Category, "file");
563		assert!(!perm.IsSensitive);
564	}
565
566	#[test]
567	fn TestPermissionNewSensitive() {
568		let perm =
569			Permission::NewSensitive("config.update".to_string(), "Update config".to_string(), "config".to_string());
570
571		assert!(perm.IsSensitive);
572	}
573
574	#[test]
575	fn TestPermissionGetAction() {
576		let perm = Permission::New("file.read".to_string(), "Read files".to_string(), "file".to_string());
577
578		assert_eq!(perm.GetAction(), "read");
579	}
580
581	#[test]
582	fn TestPermissionGetCategory() {
583		let perm = Permission::New("file.read".to_string(), "Read files".to_string(), "file".to_string());
584
585		assert_eq!(perm.GetCategory(), "file");
586	}
587
588	#[test]
589	fn TestPermissionValidateSuccess() {
590		let perm = Permission::New("file.read".to_string(), "Read files".to_string(), "file".to_string());
591
592		assert!(perm.Validate().is_ok());
593	}
594
595	#[test]
596	fn TestPermissionValidateMissingDot() {
597		let perm = Permission::New("fileread".to_string(), "Read files".to_string(), "file".to_string());
598
599		assert!(perm.Validate().is_err());
600	}
601
602	#[test]
603	fn TestCreateStandardRoles() {
604		let roles = CreateStandardRoles();
605		assert_eq!(roles.len(), 3);
606
607		let user_role = roles.iter().find(|r| r.Name == "user").unwrap();
608		assert!(user_role.HasPermission("file.read"));
609
610		let admin_role = roles.iter().find(|r| r.Name == "admin").unwrap();
611		assert!(admin_role.HasPermission("admin.manage"));
612	}
613
614	#[test]
615	fn TestCreateStandardPermissions() {
616		let perms = CreateStandardPermissions();
617		assert!(perms.len() > 0);
618
619		let file_read = perms.iter().find(|p| p.Name == "file.read").unwrap();
620		assert_eq!(file_read.Category, "file");
621
622		let config_update = perms.iter().find(|p| p.Name == "config.update").unwrap();
623		assert!(config_update.IsSensitive);
624	}
625
626	#[test]
627	fn TestRoleWithParent() {
628		let role = Role::NewWithParent(
629			"test.role".to_string(),
630			vec!["perm1".to_string()],
631			"Test role".to_string(),
632			"parent.role".to_string(),
633			10,
634		);
635
636		assert_eq!(role.ParentRole, Some("parent.role".to_string()));
637		assert_eq!(role.Priority, 10);
638	}
639}