Skip to main content

Mountain/IPC/Permission/Validate/ValidatePermission/
Validator.rs

1#![allow(non_snake_case)]
2
3//! `Validator::Struct` - role-based access control engine.
4//! Holds the role / permission tables and the operation →
5//! required-permission mapping; enforces the
6//! default-deny + RBAC policy through `ValidatePermission`.
7//! The struct + impl + tests stay in one file - tightly
8//! coupled cluster.
9
10use std::{
11	collections::HashMap,
12	sync::Arc,
13	time::{Duration, SystemTime},
14};
15
16use tokio::sync::RwLock;
17
18use crate::{
19	IPC::Permission::{
20		Role::ManageRole::{Permission::Struct as Permission, Role::Struct as Role},
21		Validate::ValidatePermission::SecurityContext::Struct as SecurityContext,
22	},
23	dev_log,
24};
25
26pub struct Struct {
27	pub(super) Roles:Arc<RwLock<HashMap<String, Role>>>,
28	pub(super) Permissions:Arc<RwLock<HashMap<String, Permission>>>,
29	pub(super) OperationPermissions:HashMap<String, Vec<String>>,
30	pub(super) ValidationTimeoutMillis:u64,
31}
32
33impl Struct {
34	pub fn New(ValidationTimeoutMillis:u64) -> Self {
35		Self {
36			Roles:Arc::new(RwLock::new(HashMap::new())),
37			Permissions:Arc::new(RwLock::new(HashMap::new())),
38			OperationPermissions:Self::BuildOperationMapping(),
39			ValidationTimeoutMillis,
40		}
41	}
42
43	fn BuildOperationMapping() -> HashMap<String, Vec<String>> {
44		let mut mapping = HashMap::new();
45		mapping.insert("file:write".to_string(), vec!["file.write".to_string()]);
46		mapping.insert("file:delete".to_string(), vec!["file.write".to_string()]);
47		mapping.insert("file:read".to_string(), vec!["file.read".to_string()]);
48		mapping.insert("configuration:update".to_string(), vec!["config.update".to_string()]);
49		mapping.insert("configuration:read".to_string(), vec!["config.read".to_string()]);
50		mapping.insert("storage:set".to_string(), vec!["storage.write".to_string()]);
51		mapping.insert("storage:get".to_string(), vec!["storage.read".to_string()]);
52		mapping.insert("native:openExternal".to_string(), vec!["system.external".to_string()]);
53		mapping.insert("system:execute".to_string(), vec!["system.execute".to_string()]);
54		mapping.insert("admin:manage".to_string(), vec!["admin.manage".to_string()]);
55		mapping
56	}
57
58	pub fn CreateSecurityContext(
59		UserId:String,
60		Roles:Vec<String>,
61		IpAddress:String,
62		DirectPermissions:Vec<String>,
63	) -> SecurityContext {
64		let ValidRoles = if Roles.is_empty() { vec!["user".to_string()] } else { Roles };
65		let ValidIpAddress = if IpAddress.is_empty() { "127.0.0.1".to_string() } else { IpAddress };
66
67		SecurityContext {
68			UserId,
69			Roles:ValidRoles,
70			Permissions:DirectPermissions,
71			IpAddress:ValidIpAddress,
72			Timestamp:SystemTime::now(),
73		}
74	}
75
76	pub async fn ValidatePermission(&self, Operation:&str, Context:&SecurityContext) -> Result<(), String> {
77		let timeout_duration = Duration::from_millis(self.ValidationTimeoutMillis);
78
79		let result = tokio::time::timeout(timeout_duration, async {
80			self.ValidatePermissionInternal(Operation, Context).await
81		})
82		.await;
83
84		match result {
85			Ok(validation_result) => validation_result,
86			Err(_) => {
87				dev_log!(
88					"ipc",
89					"error: [PermissionValidator] Permission validation timed out for operation: {}",
90					Operation
91				);
92				Err("Permission validation timeout".to_string())
93			},
94		}
95	}
96
97	async fn ValidatePermissionInternal(&self, Operation:&str, Context:&SecurityContext) -> Result<(), String> {
98		if Operation.is_empty() {
99			return Err("Operation name cannot be empty".to_string());
100		}
101
102		if Context.UserId.is_empty() {
103			return Err("User ID cannot be empty".to_string());
104		}
105
106		if Context.Roles.is_empty() && Context.Permissions.is_empty() {
107			return Err("User has no assigned roles or permissions".to_string());
108		}
109
110		let RequiredPermissions = match self.OperationPermissions.get(Operation) {
111			Some(perms) => perms.clone(),
112			None => return Ok(()),
113		};
114
115		if RequiredPermissions.is_empty() {
116			return Ok(());
117		}
118
119		let UserPermissions = self.AggregateUserPermissions(Context).await?;
120
121		for RequiredPermission in &RequiredPermissions {
122			if !UserPermissions.contains(RequiredPermission) {
123				return Err(format!("Missing required permission: {}", RequiredPermission));
124			}
125		}
126
127		Ok(())
128	}
129
130	async fn AggregateUserPermissions(&self, Context:&SecurityContext) -> Result<Vec<String>, String> {
131		let mut UserPermissions:Vec<String> = Context.Permissions.clone();
132
133		let roles_read = self.Roles.read().await;
134		for RoleName in &Context.Roles {
135			if let Some(role) = roles_read.get(RoleName) {
136				for Permission in &role.Permissions {
137					if !UserPermissions.contains(Permission) {
138						UserPermissions.push(Permission.clone());
139					}
140				}
141			} else {
142				dev_log!("ipc", "[PermissionValidator] Role not found: {}, skipping", RoleName);
143			}
144		}
145
146		Ok(UserPermissions)
147	}
148
149	pub async fn RegisterRole(&self, Role:Role) -> Result<(), String> {
150		if Role.Name.is_empty() {
151			return Err("Role name cannot be empty".to_string());
152		}
153
154		let mut roles = self.Roles.write().await;
155
156		let permissions_read = self.Permissions.read().await;
157		for PermissionName in &Role.Permissions {
158			if !permissions_read.contains_key(PermissionName) {
159				dev_log!(
160					"ipc",
161					"warn: [PermissionValidator] Permission '{}' referenced by role '{}' does not exist",
162					PermissionName,
163					Role.Name
164				);
165			}
166		}
167		drop(permissions_read);
168
169		let RoleName = Role.Name.clone();
170		roles.insert(RoleName.clone(), Role);
171		dev_log!("ipc", "[PermissionValidator] Role registered: {}", RoleName);
172		Ok(())
173	}
174
175	pub async fn RegisterPermission(&self, Permission:Permission) -> Result<(), String> {
176		if Permission.Name.is_empty() {
177			return Err("Permission name cannot be empty".to_string());
178		}
179
180		if Permission.Description.is_empty() {
181			return Err("Permission description cannot be empty".to_string());
182		}
183
184		let mut permissions = self.Permissions.write().await;
185		let PermissionName = Permission.Name.clone();
186		permissions.insert(PermissionName.clone(), Permission);
187		dev_log!("ipc", "[PermissionValidator] Permission registered: {}", PermissionName);
188		Ok(())
189	}
190
191	pub async fn GetRolePermissions(&self, RoleName:&str) -> Vec<String> {
192		let roles = self.Roles.read().await;
193		roles.get(RoleName).map(|role| role.Permissions.clone()).unwrap_or_default()
194	}
195
196	pub async fn HasPermission(&self, Context:&SecurityContext, PermissionName:&str) -> bool {
197		if Context.Permissions.contains(&PermissionName.to_string()) {
198			return true;
199		}
200
201		let roles = self.Roles.read().await;
202		for RoleName in &Context.Roles {
203			if let Some(role) = roles.get(RoleName) {
204				if role.Permissions.contains(&PermissionName.to_string()) {
205					return true;
206				}
207			}
208		}
209
210		false
211	}
212
213	pub async fn InitializeDefaults(&self) -> Result<(), String> {
214		dev_log!("ipc", "[PermissionValidator] Initializing default roles and permissions");
215
216		let DefaultPermissions = vec![
217			Permission {
218				Name:"file.read".to_string(),
219				Description:"Read file operations".to_string(),
220				Category:"file".to_string(),
221				IsSensitive:false,
222			},
223			Permission {
224				Name:"file.write".to_string(),
225				Description:"Write file operations".to_string(),
226				Category:"file".to_string(),
227				IsSensitive:false,
228			},
229			Permission {
230				Name:"config.read".to_string(),
231				Description:"Read configuration".to_string(),
232				Category:"config".to_string(),
233				IsSensitive:false,
234			},
235			Permission {
236				Name:"config.update".to_string(),
237				Description:"Update configuration".to_string(),
238				Category:"config".to_string(),
239				IsSensitive:false,
240			},
241			Permission {
242				Name:"storage.read".to_string(),
243				Description:"Read storage".to_string(),
244				Category:"storage".to_string(),
245				IsSensitive:false,
246			},
247			Permission {
248				Name:"storage.write".to_string(),
249				Description:"Write storage".to_string(),
250				Category:"storage".to_string(),
251				IsSensitive:false,
252			},
253			Permission {
254				Name:"system.external".to_string(),
255				Description:"Access external system resources".to_string(),
256				Category:"system".to_string(),
257				IsSensitive:true,
258			},
259			Permission {
260				Name:"system.execute".to_string(),
261				Description:"Execute system commands".to_string(),
262				Category:"system".to_string(),
263				IsSensitive:true,
264			},
265			Permission {
266				Name:"admin.manage".to_string(),
267				Description:"Administrative management operations".to_string(),
268				Category:"admin".to_string(),
269				IsSensitive:true,
270			},
271		];
272
273		for Permission in DefaultPermissions {
274			self.RegisterPermission(Permission).await?;
275		}
276
277		let DefaultRoles = vec![
278			Role {
279				Name:"user".to_string(),
280				Permissions:vec!["file.read".to_string(), "config.read".to_string(), "storage.read".to_string()],
281				Description:"Standard user with read access".to_string(),
282				ParentRole:None,
283				Priority:0,
284			},
285			Role {
286				Name:"developer".to_string(),
287				Permissions:vec![
288					"file.read".to_string(),
289					"file.write".to_string(),
290					"config.read".to_string(),
291					"storage.read".to_string(),
292					"storage.write".to_string(),
293				],
294				Description:"Developer with read/write access".to_string(),
295				ParentRole:None,
296				Priority:1,
297			},
298			Role {
299				Name:"admin".to_string(),
300				Permissions:vec![
301					"file.read".to_string(),
302					"file.write".to_string(),
303					"config.read".to_string(),
304					"config.update".to_string(),
305					"storage.read".to_string(),
306					"storage.write".to_string(),
307					"system.external".to_string(),
308					"system.execute".to_string(),
309					"admin.manage".to_string(),
310				],
311				Description:"Administrator with full access".to_string(),
312				ParentRole:None,
313				Priority:2,
314			},
315		];
316
317		for Role in DefaultRoles {
318			self.RegisterRole(Role).await?;
319		}
320
321		dev_log!(
322			"ipc",
323			"[PermissionValidator] Default roles and permissions initialized successfully"
324		);
325		Ok(())
326	}
327}