Skip to main content

Mountain/IPC/Permission/Role/ManageRole/
Role.rs

1
2//! `Role::Struct` - RBAC role descriptor. Builder methods
3//! deduplicate permissions on insert, expose
4//! `HasPermission` / `PermissionCount` lookups, and
5//! `Validate` enforces the `category.action` permission name
6//! shape so misconfigured roles fail loudly at registration.
7
8use std::collections::HashSet;
9
10use serde::{Deserialize, Serialize};
11
12use crate::dev_log;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Struct {
16	pub Name:String,
17
18	pub Permissions:Vec<String>,
19
20	pub Description:String,
21
22	pub ParentRole:Option<String>,
23
24	pub Priority:u32,
25}
26
27impl Struct {
28	pub fn New(Name:String, Permissions:Vec<String>, Description:String) -> Self {
29		let UniquePermissions:Vec<String> = Permissions.into_iter().collect::<HashSet<String>>().into_iter().collect();
30
31		Self { Name, Permissions:UniquePermissions, Description, ParentRole:None, Priority:0 }
32	}
33
34	pub fn NewWithParent(
35		Name:String,
36
37		Permissions:Vec<String>,
38
39		Description:String,
40
41		ParentRole:String,
42
43		Priority:u32,
44	) -> Self {
45		let UniquePermissions:Vec<String> = Permissions.into_iter().collect::<HashSet<String>>().into_iter().collect();
46
47		Self {
48			Name,
49
50			Permissions:UniquePermissions,
51
52			Description,
53
54			ParentRole:Some(ParentRole),
55
56			Priority,
57		}
58	}
59
60	pub fn AddPermission(mut self, Permission:String) -> Self {
61		if !self.Permissions.contains(&Permission) {
62			self.Permissions.push(Permission.clone());
63
64			dev_log!("ipc", "[Role] Added permission '{}' to role '{}'", Permission, self.Name);
65		}
66
67		self
68	}
69
70	pub fn AddPermissions(mut self, Permissions:impl IntoIterator<Item = String>) -> Self {
71		for Permission in Permissions {
72			if !self.Permissions.contains(&Permission) {
73				self.Permissions.push(Permission.clone());
74
75				dev_log!("ipc", "[Role] Added permission '{}' to role '{}'", Permission, self.Name);
76			}
77		}
78
79		self
80	}
81
82	pub fn HasPermission(&self, Permission:&str) -> bool { self.Permissions.contains(&Permission.to_string()) }
83
84	pub fn PermissionCount(&self) -> usize { self.Permissions.len() }
85
86	pub fn Validate(&self) -> Result<(), String> {
87		if self.Name.is_empty() {
88			return Err("Role name cannot be empty".to_string());
89		}
90
91		if self.Name.contains(|c:char| c.is_whitespace()) {
92			return Err("Role name cannot contain whitespace".to_string());
93		}
94
95		if self.Description.is_empty() {
96			return Err("Role description cannot be empty".to_string());
97		}
98
99		for Permission in &self.Permissions {
100			if Permission.is_empty() {
101				return Err("Permission name cannot be empty".to_string());
102			}
103
104			if !Permission.contains('.') {
105				return Err(format!(
106					"Permission '{}' must contain a dot separating category and action",
107					Permission
108				));
109			}
110
111			if Permission.contains(|c:char| c.is_whitespace()) {
112				return Err(format!("Permission '{}' cannot contain whitespace", Permission));
113			}
114		}
115
116		Ok(())
117	}
118}