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
19	pub Permissions:Vec<String>,
20
21	pub Description:String,
22
23	pub ParentRole:Option<String>,
24
25	pub Priority:u32,
26}
27
28impl Struct {
29	pub fn New(Name:String, Permissions:Vec<String>, Description:String) -> Self {
30		let UniquePermissions:Vec<String> = Permissions.into_iter().collect::<HashSet<String>>().into_iter().collect();
31
32		Self { Name, Permissions:UniquePermissions, Description, ParentRole:None, Priority:0 }
33	}
34
35	pub fn NewWithParent(
36		Name:String,
37
38		Permissions:Vec<String>,
39
40		Description:String,
41
42		ParentRole:String,
43
44		Priority:u32,
45	) -> Self {
46		let UniquePermissions:Vec<String> = Permissions.into_iter().collect::<HashSet<String>>().into_iter().collect();
47
48		Self {
49			Name,
50
51			Permissions:UniquePermissions,
52
53			Description,
54
55			ParentRole:Some(ParentRole),
56
57			Priority,
58		}
59	}
60
61	pub fn AddPermission(mut self, Permission:String) -> Self {
62		if !self.Permissions.contains(&Permission) {
63			self.Permissions.push(Permission.clone());
64
65			dev_log!("ipc", "[Role] Added permission '{}' to role '{}'", Permission, self.Name);
66		}
67
68		self
69	}
70
71	pub fn AddPermissions(mut self, Permissions:impl IntoIterator<Item = String>) -> Self {
72		for Permission in Permissions {
73			if !self.Permissions.contains(&Permission) {
74				self.Permissions.push(Permission.clone());
75
76				dev_log!("ipc", "[Role] Added permission '{}' to role '{}'", Permission, self.Name);
77			}
78		}
79
80		self
81	}
82
83	pub fn HasPermission(&self, Permission:&str) -> bool { self.Permissions.contains(&Permission.to_string()) }
84
85	pub fn PermissionCount(&self) -> usize { self.Permissions.len() }
86
87	pub fn Validate(&self) -> Result<(), String> {
88		if self.Name.is_empty() {
89			return Err("Role name cannot be empty".to_string());
90		}
91
92		if self.Name.contains(|c:char| c.is_whitespace()) {
93			return Err("Role name cannot contain whitespace".to_string());
94		}
95
96		if self.Description.is_empty() {
97			return Err("Role description cannot be empty".to_string());
98		}
99
100		for Permission in &self.Permissions {
101			if Permission.is_empty() {
102				return Err("Permission name cannot be empty".to_string());
103			}
104
105			if !Permission.contains('.') {
106				return Err(format!(
107					"Permission '{}' must contain a dot separating category and action",
108					Permission
109				));
110			}
111
112			if Permission.contains(|c:char| c.is_whitespace()) {
113				return Err(format!("Permission '{}' cannot contain whitespace", Permission));
114			}
115		}
116
117		Ok(())
118	}
119}