Skip to main content

Mountain/IPC/Permission/Validate/
ValidatePermission.rs

1//! # Validate
2//!
3//! ## File: IPC/Permission/Validate/ValidatePermission.rs
4//!
5//! ## Role in Mountain Architecture
6//!
7//! Implements role-based access control (RBAC) for IPC operations,
8//! validating that users have necessary permissions before executing
9//! operations.
10//!
11//! ## Primary Responsibility
12//!
13//! Validate user permissions for IPC operations using role-based access
14//! control.
15//!
16//! ## Secondary Responsibilities
17//!
18//! - Create security context from incoming messages
19//! - Map operations to required permissions
20//! - Aggregate permissions from user roles
21//! - Log permission validation results
22//!
23//! ## Dependencies
24//!
25//! **External Crates:**
26//! - `std::collections::HashMap` - Role and permission storage
27//! - `tokio::sync::RwLock` - Async-safe concurrent access
28//! - `log` - Validation event logging
29//! - `serde` - Serialization for audit trails
30//!
31//! **Internal Modules:**
32//! - `ManageRole::{Role, Permission}` - Role and permission definitions
33//! - `LogEvent::{SecurityEvent, SecurityEventType}` - Audit logging types
34//!
35//! ## Dependents
36//!
37//! - `TauriIPCServer` - Validates permissions before message processing
38//! - `RouteMessage` - Routes only authorized messages
39//!
40//! ## VSCode Pattern Reference
41//!
42//! Matches VSCode's role-based permissions in `vs/base/common/permissions.ts`
43//! - Hierarchical permission system
44//! - Role-based access control
45//! - Permission inheritance through role hierarchy
46//! - Operation-to-permission mapping
47//!
48//! ## Security Considerations
49//!
50//! - RBAC prevents unauthorized access to sensitive operations
51//! - All permission checks performed server-side (never trust client)
52//! - Audit logging for security compliance
53//! - Permission validation failures do not leak system internals
54//! - Default-deny policy (explicit deny if permission not found)
55//! - Timeout on permission checks prevents blocking
56//! - Role-based inheritance for scalable permission management
57//!
58//! ## Performance Considerations
59//!
60//! - RwLock allows concurrent reads, exclusive writes
61//! - Permission caching at role level reduces redundancy
62//! - Fast HashMap lookups for permission resolution
63//! - Async operations prevent blocking main thread
64//! - Early validation fails fast to reject unauthorized requests
65//!
66//! ## Error Handling Strategy
67//!
68//! - Returns Result for explicit error handling
69//! - Detailed error messages without exposing sensitive data
70//! - Permission denied errors logged but don't crash system
71//! - Invalid context handled gracefully with default values
72//!
73//! ## Thread Safety
74//!
75//! - RwLock wrapped in Arc for safe concurrent access
76//! - Multiple concurrent reads, exclusive writes
77//! - Lock contention minimized by short critical sections
78//!
79//! ## TODO Items
80//!
81//! - [ ] Implement role hierarchy (roles can inherit from parent roles)
82//! - [ ] Add permission caching with TTL for frequently accessed permissions
83//! - [ ] Support permission negation (explicit deny overrides allow)
84//! - [ ] Add rate limiting for permission checks
85
86use std::{
87	collections::HashMap,
88	sync::Arc,
89	time::{Duration, SystemTime},
90};
91
92use tokio::sync::RwLock;
93use serde::{Deserialize, Serialize};
94
95use super::super::Role::ManageRole::{Permission, Role};
96use crate::dev_log;
97
98/// Security context for permission validation
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct SecurityContext {
101	/// Unique user identifier
102	pub UserId:String,
103
104	/// User's assigned roles
105	pub Roles:Vec<String>,
106
107	/// Direct user permissions (if any)
108	pub Permissions:Vec<String>,
109
110	/// Origin IP address for IP-based restrictions
111	pub IpAddress:String,
112
113	/// Request timestamp for time-based restrictions
114	pub Timestamp:SystemTime,
115}
116
117/// Permission validator for IPC operations
118pub struct PermissionValidator {
119	/// Role definitions indexed by role name
120	Roles:Arc<RwLock<HashMap<String, Role>>>,
121
122	/// Permission definitions indexed by permission name
123	Permissions:Arc<RwLock<HashMap<String, Permission>>>,
124
125	/// Operation to permission mapping
126	OperationPermissions:HashMap<String, Vec<String>>,
127
128	/// Maximum time allowed for permission validation (milliseconds)
129	ValidationTimeoutMillis:u64,
130}
131
132impl PermissionValidator {
133	/// Create a new permission validator
134	///
135	/// ## Parameters
136	/// - `ValidationTimeoutMillis`: Maximum timeout for validation in
137	///   milliseconds
138	///
139	/// ## Returns
140	/// New PermissionValidator instance
141	pub fn New(ValidationTimeoutMillis:u64) -> Self {
142		let OperationPermissions = Self::BuildOperationMapping();
143
144		Self {
145			Roles:Arc::new(RwLock::new(HashMap::new())),
146			Permissions:Arc::new(RwLock::new(HashMap::new())),
147			OperationPermissions,
148			ValidationTimeoutMillis,
149		}
150	}
151
152	/// Build operation to permission mapping
153	///
154	/// ## Returns
155	/// HashMap mapping operation names to required permission strings
156	fn BuildOperationMapping() -> HashMap<String, Vec<String>> {
157		let mut mapping = HashMap::new();
158
159		mapping.insert("file:write".to_string(), vec!["file.write".to_string()]);
160		mapping.insert("file:delete".to_string(), vec!["file.write".to_string()]);
161		mapping.insert("file:read".to_string(), vec!["file.read".to_string()]);
162		mapping.insert("configuration:update".to_string(), vec!["config.update".to_string()]);
163		mapping.insert("configuration:read".to_string(), vec!["config.read".to_string()]);
164		mapping.insert("storage:set".to_string(), vec!["storage.write".to_string()]);
165		mapping.insert("storage:get".to_string(), vec!["storage.read".to_string()]);
166		mapping.insert("native:openExternal".to_string(), vec!["system.external".to_string()]);
167		mapping.insert("system:execute".to_string(), vec!["system.execute".to_string()]);
168		mapping.insert("admin:manage".to_string(), vec!["admin.manage".to_string()]);
169
170		mapping
171	}
172
173	/// Create security context from message data
174	///
175	/// ## Parameters
176	/// - `UserId`: User identifier for the request
177	/// - `Roles`: User's assigned roles (defaults to ["user"] if empty)
178	/// - `IpAddress`: Origin IP address
179	/// - `DirectPermissions`: Direct user permissions (optional)
180	///
181	/// ## Returns
182	/// New SecurityContext instance
183	pub fn CreateSecurityContext(
184		UserId:String,
185		Roles:Vec<String>,
186		IpAddress:String,
187		DirectPermissions:Vec<String>,
188	) -> SecurityContext {
189		let ValidRoles = if Roles.is_empty() { vec!["user".to_string()] } else { Roles };
190
191		let ValidIpAddress = if IpAddress.is_empty() { "127.0.0.1".to_string() } else { IpAddress };
192
193		SecurityContext {
194			UserId,
195			Roles:ValidRoles,
196			Permissions:DirectPermissions,
197			IpAddress:ValidIpAddress,
198			Timestamp:SystemTime::now(),
199		}
200	}
201
202	/// Validate permission for an operation with security context
203	///
204	/// ## Parameters
205	/// - `Operation`: The operation being performed
206	/// - `Context`: Security context containing user info and roles
207	///
208	/// ## Returns
209	/// Ok(()) if permission granted, Err with message if denied
210	///
211	/// ## Security Notes
212	/// - All operations require explicit permission grant (default deny)
213	/// - Validation is performed server-side only
214	/// - IP address can be used for additional restrictions
215	/// - Timestamp can be used for time-based restrictions
216	pub async fn ValidatePermission(&self, Operation:&str, Context:&SecurityContext) -> Result<(), String> {
217		// Start timeout timer
218		let timeout_duration = Duration::from_millis(self.ValidationTimeoutMillis);
219
220		// Use tokio::time::timeout for async timeout
221		let result = tokio::time::timeout(timeout_duration, async {
222			self.ValidatePermissionInternal(Operation, Context).await
223		})
224		.await;
225
226		match result {
227			Ok(validation_result) => validation_result,
228			Err(_) => {
229				dev_log!(
230					"ipc",
231					"error: [PermissionValidator] Permission validation timed out for operation: {}",
232					Operation
233				);
234				Err("Permission validation timeout".to_string())
235			},
236		}
237	}
238
239	/// Internal validation logic (without timeout wrapper)
240	///
241	/// ## Parameters
242	/// - `Operation`: The operation being performed
243	/// - `Context`: Security context
244	///
245	/// ## Returns
246	/// Ok(()) if permission granted, Err with message if denied
247	async fn ValidatePermissionInternal(&self, Operation:&str, Context:&SecurityContext) -> Result<(), String> {
248		// Validate inputs
249		if Operation.is_empty() {
250			dev_log!("ipc", "warn: [PermissionValidator] Empty operation name provided");
251			return Err("Operation name cannot be empty".to_string());
252		}
253
254		if Context.UserId.is_empty() {
255			dev_log!("ipc", "warn: [PermissionValidator] Empty user ID in security context");
256			return Err("User ID cannot be empty".to_string());
257		}
258
259		if Context.Roles.is_empty() && Context.Permissions.is_empty() {
260			dev_log!(
261				"ipc",
262				"warn: [PermissionValidator] User has no roles or permissions: {}",
263				Context.UserId
264			);
265			return Err("User has no assigned roles or permissions".to_string());
266		}
267
268		// Get required permissions for this operation
269		let RequiredPermissions = match self.OperationPermissions.get(Operation) {
270			Some(perms) => perms.clone(),
271			None => {
272				// No specific permissions required for this operation
273				dev_log!(
274					"ipc",
275					"[PermissionValidator] No specific permissions required for operation: {}",
276					Operation
277				);
278				return Ok(());
279			},
280		};
281
282		if RequiredPermissions.is_empty() {
283			// No permissions needed
284			dev_log!(
285				"ipc",
286				"[PermissionValidator] Access granted (no permissions required): {} by {}",
287				Operation,
288				Context.UserId
289			);
290			Ok(())
291		} else {
292			// Aggregate all user permissions
293			let UserPermissions = self.AggregateUserPermissions(Context).await?;
294
295			// Check if user has all required permissions
296			for RequiredPermission in &RequiredPermissions {
297				if !UserPermissions.contains(RequiredPermission) {
298					dev_log!(
299						"ipc",
300						"warn: [PermissionValidator] Permission denied: {} required, user {} has {:?}",
301						RequiredPermission,
302						Context.UserId,
303						UserPermissions
304					);
305					return Err(format!("Missing required permission: {}", RequiredPermission));
306				}
307			}
308
309			// All permissions granted
310			dev_log!(
311				"ipc",
312				"[PermissionValidator] Access granted: {} by {}",
313				Operation,
314				Context.UserId
315			);
316			Ok(())
317		}
318	}
319
320	/// Aggregate all permissions for a user from roles and direct permissions
321	///
322	/// ## Parameters
323	/// - `Context`: Security context containing roles and direct permissions
324	///
325	/// ## Returns
326	/// Vector of all permission strings available to the user
327	async fn AggregateUserPermissions(&self, Context:&SecurityContext) -> Result<Vec<String>, String> {
328		let mut UserPermissions:Vec<String> = Context.Permissions.clone();
329
330		// Collect permissions from all roles
331		let roles_read = self.Roles.read().await;
332		for RoleName in &Context.Roles {
333			if let Some(role) = roles_read.get(RoleName) {
334				for Permission in &role.Permissions {
335					if !UserPermissions.contains(Permission) {
336						UserPermissions.push(Permission.clone());
337					}
338				}
339			} else {
340				dev_log!("ipc", "[PermissionValidator] Role not found: {}, skipping", RoleName);
341			}
342		}
343
344		Ok(UserPermissions)
345	}
346
347	/// Register a role definition
348	///
349	/// ## Parameters
350	/// - `Role`: Role definition to register
351	///
352	/// ## Returns
353	/// Result indicating success or error
354	pub async fn RegisterRole(&self, Role:Role) -> Result<(), String> {
355		// Validate role
356		if Role.Name.is_empty() {
357			return Err("Role name cannot be empty".to_string());
358		}
359
360		if Role.Permissions.is_empty() {
361			dev_log!("ipc", "warn: [PermissionValidator] Role '{}' has no permissions", Role.Name);
362		}
363
364		let mut roles = self.Roles.write().await;
365
366		// Validate that all referenced permissions exist
367		let permissions_read = self.Permissions.read().await;
368		for PermissionName in &Role.Permissions {
369			if !permissions_read.contains_key(PermissionName) {
370				dev_log!(
371					"ipc",
372					"warn: [PermissionValidator] Permission '{}' referenced by role '{}' does not exist",
373					PermissionName,
374					Role.Name
375				);
376			}
377		}
378		drop(permissions_read);
379
380		let RoleName = Role.Name.clone();
381		roles.insert(RoleName.clone(), Role);
382		dev_log!("ipc", "[PermissionValidator] Role registered: {}", RoleName);
383		Ok(())
384	}
385
386	/// Register a permission definition
387	///
388	/// ## Parameters
389	/// - `Permission`: Permission definition to register
390	///
391	/// /// Returns
392	/// Result indicating success or error
393	pub async fn RegisterPermission(&self, Permission:Permission) -> Result<(), String> {
394		// Validate permission
395		if Permission.Name.is_empty() {
396			return Err("Permission name cannot be empty".to_string());
397		}
398
399		if Permission.Description.is_empty() {
400			return Err("Permission description cannot be empty".to_string());
401		}
402
403		let mut permissions = self.Permissions.write().await;
404		let PermissionName = Permission.Name.clone();
405		permissions.insert(PermissionName.clone(), Permission);
406		dev_log!("ipc", "[PermissionValidator] Permission registered: {}", PermissionName);
407		Ok(())
408	}
409
410	/// Get all permissions for a specific role
411	///
412	/// ## Parameters
413	/// - `RoleName`: Name of the role to query
414	///
415	/// ## Returns
416	/// Vector of permission strings for the role, empty if role not found
417	pub async fn GetRolePermissions(&self, RoleName:&str) -> Vec<String> {
418		let roles = self.Roles.read().await;
419		roles.get(RoleName).map(|role| role.Permissions.clone()).unwrap_or_default()
420	}
421
422	/// Check if a user has a specific permission
423	///
424	/// ## Parameters
425	/// - `Context`: Security context for the user
426	/// - `PermissionName`: Permission name to check
427	///
428	/// /// Returns
429	/// true if user has permission, false otherwise
430	pub async fn HasPermission(&self, Context:&SecurityContext, PermissionName:&str) -> bool {
431		// Check direct permissions
432		if Context.Permissions.contains(&PermissionName.to_string()) {
433			return true;
434		}
435
436		// Check role permissions
437		let roles = self.Roles.read().await;
438		for RoleName in &Context.Roles {
439			if let Some(role) = roles.get(RoleName) {
440				if role.Permissions.contains(&PermissionName.to_string()) {
441					return true;
442				}
443			}
444		}
445
446		false
447	}
448
449	/// Initialize default roles and permissions
450	///
451	/// ## Returns
452	/// Result indicating success or error
453	///
454	/// ## Default Roles
455	/// - `user`: Read-only access to files, config, storage
456	/// - `developer`: Read/write access to files and storage
457	/// - `admin`: Full access including system operations
458	pub async fn InitializeDefaults(&self) -> Result<(), String> {
459		dev_log!("ipc", "[PermissionValidator] Initializing default roles and permissions");
460
461		// Define default permissions
462		let DefaultPermissions = vec![
463			Permission {
464				Name:"file.read".to_string(),
465				Description:"Read file operations".to_string(),
466				Category:"file".to_string(),
467				IsSensitive:false,
468			},
469			Permission {
470				Name:"file.write".to_string(),
471				Description:"Write file operations".to_string(),
472				Category:"file".to_string(),
473				IsSensitive:false,
474			},
475			Permission {
476				Name:"config.read".to_string(),
477				Description:"Read configuration".to_string(),
478				Category:"config".to_string(),
479				IsSensitive:false,
480			},
481			Permission {
482				Name:"config.update".to_string(),
483				Description:"Update configuration".to_string(),
484				Category:"config".to_string(),
485				IsSensitive:false,
486			},
487			Permission {
488				Name:"storage.read".to_string(),
489				Description:"Read storage".to_string(),
490				Category:"storage".to_string(),
491				IsSensitive:false,
492			},
493			Permission {
494				Name:"storage.write".to_string(),
495				Description:"Write storage".to_string(),
496				Category:"storage".to_string(),
497				IsSensitive:false,
498			},
499			Permission {
500				Name:"system.external".to_string(),
501				Description:"Access external system resources".to_string(),
502				Category:"system".to_string(),
503				IsSensitive:true,
504			},
505			Permission {
506				Name:"system.execute".to_string(),
507				Description:"Execute system commands".to_string(),
508				Category:"system".to_string(),
509				IsSensitive:true,
510			},
511			Permission {
512				Name:"admin.manage".to_string(),
513				Description:"Administrative management operations".to_string(),
514				Category:"admin".to_string(),
515				IsSensitive:true,
516			},
517		];
518
519		// Register permissions
520		for Permission in DefaultPermissions {
521			self.RegisterPermission(Permission).await?;
522		}
523
524		// Define default roles
525		let DefaultRoles = vec![
526			Role {
527				Name:"user".to_string(),
528				Permissions:vec!["file.read".to_string(), "config.read".to_string(), "storage.read".to_string()],
529				Description:"Standard user with read access".to_string(),
530				ParentRole:None,
531				Priority:0,
532			},
533			Role {
534				Name:"developer".to_string(),
535				Permissions:vec![
536					"file.read".to_string(),
537					"file.write".to_string(),
538					"config.read".to_string(),
539					"storage.read".to_string(),
540					"storage.write".to_string(),
541				],
542				Description:"Developer with read/write access".to_string(),
543				ParentRole:None,
544				Priority:1,
545			},
546			Role {
547				Name:"admin".to_string(),
548				Permissions:vec![
549					"file.read".to_string(),
550					"file.write".to_string(),
551					"config.read".to_string(),
552					"config.update".to_string(),
553					"storage.read".to_string(),
554					"storage.write".to_string(),
555					"system.external".to_string(),
556					"system.execute".to_string(),
557					"admin.manage".to_string(),
558				],
559				Description:"Administrator with full access".to_string(),
560				ParentRole:None,
561				Priority:2,
562			},
563		];
564
565		// Register roles
566		for Role in DefaultRoles {
567			self.RegisterRole(Role).await?;
568		}
569
570		dev_log!(
571			"ipc",
572			"[PermissionValidator] Default roles and permissions initialized successfully"
573		);
574		Ok(())
575	}
576}
577
578#[cfg(test)]
579mod Tests {
580	use super::*;
581
582	#[tokio::test]
583	async fn TestCreateSecurityContext() {
584		let context = PermissionValidator::CreateSecurityContext(
585			"test-user".to_string(),
586			vec!["user".to_string(), "admin".to_string()],
587			"192.168.1.1".to_string(),
588			vec!["custom.permission".to_string()],
589		);
590
591		assert_eq!(context.UserId, "test-user".to_string());
592		assert_eq!(context.Roles, vec!["user".to_string(), "admin".to_string()]);
593		assert_eq!(context.IpAddress, "192.168.1.1".to_string());
594		assert_eq!(context.Permissions, vec!["custom.permission".to_string()]);
595	}
596
597	#[tokio::test]
598	async fn TestCreateSecurityContextDefaults() {
599		let context =
600			PermissionValidator::CreateSecurityContext("test-user".to_string(), vec![], "".to_string(), vec![]);
601
602		assert_eq!(context.UserId, "test-user".to_string());
603		assert_eq!(context.Roles, vec!["user".to_string()]);
604		assert_eq!(context.IpAddress, "127.0.0.1".to_string());
605	}
606
607	#[tokio::test]
608	async fn TestValidatePermissionNoPermissionsRequired() {
609		let validator = PermissionValidator::New(1000);
610		let context = SecurityContext {
611			UserId:"test-user".to_string(),
612			Roles:vec!["user".to_string()],
613			Permissions:vec![],
614			IpAddress:"127.0.0.1".to_string(),
615			Timestamp:SystemTime::now(),
616		};
617
618		let result = validator.ValidatePermission("unknown:operation", &context).await;
619		assert!(result.is_ok(), "Should succeed when no permissions are required");
620	}
621
622	#[tokio::test]
623	async fn TestValidatePermissionMissingPermission() {
624		let validator = PermissionValidator::New(1000);
625		let context = SecurityContext {
626			UserId:"test-user".to_string(),
627			Roles:vec!["user".to_string()],
628			Permissions:vec![],
629			IpAddress:"127.0.0.1".to_string(),
630			Timestamp:SystemTime::now(),
631		};
632
633		let result = validator.ValidatePermission("file:write", &context).await;
634		assert!(result.is_err(), "Should fail without required permission");
635		assert!(result.unwrap_err().contains("Missing required permission"));
636	}
637
638	#[tokio::test]
639	async fn TestValidatePermissionWithDirectPermission() {
640		let validator = PermissionValidator::New(1000);
641		let context = SecurityContext {
642			UserId:"test-user".to_string(),
643			Roles:vec![],
644			Permissions:vec!["file.write".to_string()],
645			IpAddress:"127.0.0.1".to_string(),
646			Timestamp:SystemTime::now(),
647		};
648
649		let result = validator.ValidatePermission("file:write", &context).await;
650		assert!(result.is_ok(), "Should succeed with direct permission");
651	}
652
653	#[tokio::test]
654	async fn TestValidatePermissionViaRole() {
655		let validator = PermissionValidator::New(1000);
656		validator.InitializeDefaults().await.unwrap();
657
658		let context = SecurityContext {
659			UserId:"test-user".to_string(),
660			Roles:vec!["admin".to_string()],
661			Permissions:vec![],
662			IpAddress:"127.0.0.1".to_string(),
663			Timestamp:SystemTime::now(),
664		};
665
666		let result = validator.ValidatePermission("file:write", &context).await;
667		assert!(result.is_ok(), "Should succeed via role permission");
668	}
669
670	#[tokio::test]
671	async fn TestValidatePermissionEmptyOperation() {
672		let validator = PermissionValidator::New(1000);
673		let context = SecurityContext {
674			UserId:"test-user".to_string(),
675			Roles:vec!["user".to_string()],
676			Permissions:vec![],
677			IpAddress:"127.0.0.1".to_string(),
678			Timestamp:SystemTime::now(),
679		};
680
681		let result = validator.ValidatePermission("", &context).await;
682		assert!(result.is_err(), "Should fail with empty operation name");
683	}
684
685	#[tokio::test]
686	async fn TestValidatePermissionEmptyUserId() {
687		let validator = PermissionValidator::New(1000);
688		let context = SecurityContext {
689			UserId:"".to_string(),
690			Roles:vec!["user".to_string()],
691			Permissions:vec![],
692			IpAddress:"127.0.0.1".to_string(),
693			Timestamp:SystemTime::now(),
694		};
695
696		let result = validator.ValidatePermission("file:read", &context).await;
697		assert!(result.is_err(), "Should fail with empty user ID");
698	}
699
700	#[tokio::test]
701	async fn TestInitializeDefaults() {
702		let validator = PermissionValidator::New(1000);
703		let result = validator.InitializeDefaults().await;
704		assert!(result.is_ok(), "Should initialize defaults successfully");
705
706		// Check roles exist
707		let user_perms = validator.GetRolePermissions("user").await;
708		assert!(user_perms.contains(&"file.read".to_string()));
709
710		let admin_perms = validator.GetRolePermissions("admin").await;
711		assert_ne!(admin_perms.len(), 0, "Admin should have many permissions");
712	}
713
714	#[tokio::test]
715	async fn TestGetRolePermissions() {
716		let validator = PermissionValidator::New(1000);
717		validator.InitializeDefaults().await.unwrap();
718
719		let role_perms = validator.GetRolePermissions("developer").await;
720		assert!(role_perms.contains(&"file.read".to_string()));
721		assert!(role_perms.contains(&"file.write".to_string()));
722	}
723
724	#[tokio::test]
725	async fn TestHasPermissionWithRole() {
726		let validator = PermissionValidator::New(1000);
727		validator.InitializeDefaults().await.unwrap();
728
729		let context = SecurityContext {
730			UserId:"test-user".to_string(),
731			Roles:vec!["admin".to_string()],
732			Permissions:vec![],
733			IpAddress:"127.0.0.1".to_string(),
734			Timestamp:SystemTime::now(),
735		};
736
737		assert!(validator.HasPermission(&context, "file.read").await);
738		assert!(validator.HasPermission(&context, "config.update").await);
739	}
740
741	#[tokio::test]
742	async fn TestHasPermissionDirect() {
743		let validator = PermissionValidator::New(1000);
744
745		let context = SecurityContext {
746			UserId:"test-user".to_string(),
747			Roles:vec![],
748			Permissions:vec!["custom.perm".to_string()],
749			IpAddress:"127.0.0.1".to_string(),
750			Timestamp:SystemTime::now(),
751		};
752
753		assert!(validator.HasPermission(&context, "custom.perm").await);
754		assert!(!validator.HasPermission(&context, "file.read").await);
755	}
756}