Skip to main content

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

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