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 log::{debug, error, warn};
94use serde::{Deserialize, Serialize};
95
96use super::super::{
97	Audit::LogEvent::SecurityEventType,
98	Role::ManageRole::{Permission, Role},
99};
100
101/// Security context for permission validation
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct SecurityContext {
104	/// Unique user identifier
105	pub UserId:String,
106
107	/// User's assigned roles
108	pub Roles:Vec<String>,
109
110	/// Direct user permissions (if any)
111	pub Permissions:Vec<String>,
112
113	/// Origin IP address for IP-based restrictions
114	pub IpAddress:String,
115
116	/// Request timestamp for time-based restrictions
117	pub Timestamp:SystemTime,
118}
119
120/// Permission validator for IPC operations
121pub struct PermissionValidator {
122	/// Role definitions indexed by role name
123	Roles:Arc<RwLock<HashMap<String, Role>>>,
124
125	/// Permission definitions indexed by permission name
126	Permissions:Arc<RwLock<HashMap<String, Permission>>>,
127
128	/// Operation to permission mapping
129	OperationPermissions:HashMap<String, Vec<String>>,
130
131	/// Maximum time allowed for permission validation (milliseconds)
132	ValidationTimeoutMillis:u64,
133}
134
135impl PermissionValidator {
136	/// Create a new permission validator
137	///
138	/// ## Parameters
139	/// - `ValidationTimeoutMillis`: Maximum timeout for validation in
140	///   milliseconds
141	///
142	/// ## Returns
143	/// New PermissionValidator instance
144	pub fn New(ValidationTimeoutMillis:u64) -> Self {
145		let OperationPermissions = Self::BuildOperationMapping();
146
147		Self {
148			Roles:Arc::new(RwLock::new(HashMap::new())),
149			Permissions:Arc::new(RwLock::new(HashMap::new())),
150			OperationPermissions,
151			ValidationTimeoutMillis,
152		}
153	}
154
155	/// Build operation to permission mapping
156	///
157	/// ## Returns
158	/// HashMap mapping operation names to required permission strings
159	fn BuildOperationMapping() -> HashMap<String, Vec<String>> {
160		let mut mapping = HashMap::new();
161
162		mapping.insert("file:write".to_string(), vec!["file.write".to_string()]);
163		mapping.insert("file:delete".to_string(), vec!["file.write".to_string()]);
164		mapping.insert("file:read".to_string(), vec!["file.read".to_string()]);
165		mapping.insert("configuration:update".to_string(), vec!["config.update".to_string()]);
166		mapping.insert("configuration:read".to_string(), vec!["config.read".to_string()]);
167		mapping.insert("storage:set".to_string(), vec!["storage.write".to_string()]);
168		mapping.insert("storage:get".to_string(), vec!["storage.read".to_string()]);
169		mapping.insert("native:openExternal".to_string(), vec!["system.external".to_string()]);
170		mapping.insert("system:execute".to_string(), vec!["system.execute".to_string()]);
171		mapping.insert("admin:manage".to_string(), vec!["admin.manage".to_string()]);
172
173		mapping
174	}
175
176	/// Create security context from message data
177	///
178	/// ## Parameters
179	/// - `UserId`: User identifier for the request
180	/// - `Roles`: User's assigned roles (defaults to ["user"] if empty)
181	/// - `IpAddress`: Origin IP address
182	/// - `DirectPermissions`: Direct user permissions (optional)
183	///
184	/// ## Returns
185	/// New SecurityContext instance
186	pub fn CreateSecurityContext(
187		UserId:String,
188		Roles:Vec<String>,
189		IpAddress:String,
190		DirectPermissions:Vec<String>,
191	) -> SecurityContext {
192		let ValidRoles = if Roles.is_empty() { vec!["user".to_string()] } else { Roles };
193
194		let ValidIpAddress = if IpAddress.is_empty() { "127.0.0.1".to_string() } else { IpAddress };
195
196		SecurityContext {
197			UserId,
198			Roles:ValidRoles,
199			Permissions:DirectPermissions,
200			IpAddress:ValidIpAddress,
201			Timestamp:SystemTime::now(),
202		}
203	}
204
205	/// Validate permission for an operation with security context
206	///
207	/// ## Parameters
208	/// - `Operation`: The operation being performed
209	/// - `Context`: Security context containing user info and roles
210	///
211	/// ## Returns
212	/// Ok(()) if permission granted, Err with message if denied
213	///
214	/// ## Security Notes
215	/// - All operations require explicit permission grant (default deny)
216	/// - Validation is performed server-side only
217	/// - IP address can be used for additional restrictions
218	/// - Timestamp can be used for time-based restrictions
219	pub async fn ValidatePermission(&self, Operation:&str, Context:&SecurityContext) -> Result<(), String> {
220		// Start timeout timer
221		let timeout_duration = Duration::from_millis(self.ValidationTimeoutMillis);
222
223		// Use tokio::time::timeout for async timeout
224		let result = tokio::time::timeout(timeout_duration, async {
225			self.ValidatePermissionInternal(Operation, Context).await
226		})
227		.await;
228
229		match result {
230			Ok(validation_result) => validation_result,
231			Err(_) => {
232				error!(
233					"[PermissionValidator] Permission validation timed out for operation: {}",
234					Operation
235				);
236				Err("Permission validation timeout".to_string())
237			},
238		}
239	}
240
241	/// Internal validation logic (without timeout wrapper)
242	///
243	/// ## Parameters
244	/// - `Operation`: The operation being performed
245	/// - `Context`: Security context
246	///
247	/// ## Returns
248	/// Ok(()) if permission granted, Err with message if denied
249	async fn ValidatePermissionInternal(&self, Operation:&str, Context:&SecurityContext) -> Result<(), String> {
250		// Validate inputs
251		if Operation.is_empty() {
252			warn!("[PermissionValidator] Empty operation name provided");
253			return Err("Operation name cannot be empty".to_string());
254		}
255
256		if Context.UserId.is_empty() {
257			warn!("[PermissionValidator] Empty user ID in security context");
258			return Err("User ID cannot be empty".to_string());
259		}
260
261		if Context.Roles.is_empty() && Context.Permissions.is_empty() {
262			warn!("[PermissionValidator] User has no roles or permissions: {}", Context.UserId);
263			return Err("User has no assigned roles or permissions".to_string());
264		}
265
266		// Get required permissions for this operation
267		let RequiredPermissions = match self.OperationPermissions.get(Operation) {
268			Some(perms) => perms.clone(),
269			None => {
270				// No specific permissions required for this operation
271				debug!(
272					"[PermissionValidator] No specific permissions required for operation: {}",
273					Operation
274				);
275				return Ok(());
276			},
277		};
278
279		if RequiredPermissions.is_empty() {
280			// No permissions needed
281			debug!(
282				"[PermissionValidator] Access granted (no permissions required): {} by {}",
283				Operation, Context.UserId
284			);
285			Ok(())
286		} else {
287			// Aggregate all user permissions
288			let UserPermissions = self.AggregateUserPermissions(Context).await?;
289
290			// Check if user has all required permissions
291			for RequiredPermission in &RequiredPermissions {
292				if !UserPermissions.contains(RequiredPermission) {
293					warn!(
294						"[PermissionValidator] Permission denied: {} required, user {} has {:?}",
295						RequiredPermission, Context.UserId, UserPermissions
296					);
297					return Err(format!("Missing required permission: {}", RequiredPermission));
298				}
299			}
300
301			// All permissions granted
302			debug!("[PermissionValidator] Access granted: {} by {}", Operation, Context.UserId);
303			Ok(())
304		}
305	}
306
307	/// Aggregate all permissions for a user from roles and direct permissions
308	///
309	/// ## Parameters
310	/// - `Context`: Security context containing roles and direct permissions
311	///
312	/// ## Returns
313	/// Vector of all permission strings available to the user
314	async fn AggregateUserPermissions(&self, Context:&SecurityContext) -> Result<Vec<String>, String> {
315		let mut UserPermissions:Vec<String> = Context.Permissions.clone();
316
317		// Collect permissions from all roles
318		let roles_read = self.Roles.read().await;
319		for RoleName in &Context.Roles {
320			if let Some(role) = roles_read.get(RoleName) {
321				for Permission in &role.Permissions {
322					if !UserPermissions.contains(Permission) {
323						UserPermissions.push(Permission.clone());
324					}
325				}
326			} else {
327				debug!("[PermissionValidator] Role not found: {}, skipping", RoleName);
328			}
329		}
330
331		Ok(UserPermissions)
332	}
333
334	/// Register a role definition
335	///
336	/// ## Parameters
337	/// - `Role`: Role definition to register
338	///
339	/// ## Returns
340	/// Result indicating success or error
341	pub async fn RegisterRole(&self, Role:Role) -> Result<(), String> {
342		// Validate role
343		if Role.Name.is_empty() {
344			return Err("Role name cannot be empty".to_string());
345		}
346
347		if Role.Permissions.is_empty() {
348			warn!("[PermissionValidator] Role '{}' has no permissions", Role.Name);
349		}
350
351		let mut roles = self.Roles.write().await;
352
353		// Validate that all referenced permissions exist
354		let permissions_read = self.Permissions.read().await;
355		for PermissionName in &Role.Permissions {
356			if !permissions_read.contains_key(PermissionName) {
357				warn!(
358					"[PermissionValidator] Permission '{}' referenced by role '{}' does not exist",
359					PermissionName, Role.Name
360				);
361			}
362		}
363		drop(permissions_read);
364
365		let RoleName = Role.Name.clone();
366		roles.insert(RoleName.clone(), Role);
367		debug!("[PermissionValidator] Role registered: {}", RoleName);
368		Ok(())
369	}
370
371	/// Register a permission definition
372	///
373	/// ## Parameters
374	/// - `Permission`: Permission definition to register
375	///
376	/// /// Returns
377	/// Result indicating success or error
378	pub async fn RegisterPermission(&self, Permission:Permission) -> Result<(), String> {
379		// Validate permission
380		if Permission.Name.is_empty() {
381			return Err("Permission name cannot be empty".to_string());
382		}
383
384		if Permission.Description.is_empty() {
385			return Err("Permission description cannot be empty".to_string());
386		}
387
388		let mut permissions = self.Permissions.write().await;
389		let PermissionName = Permission.Name.clone();
390		permissions.insert(PermissionName.clone(), Permission);
391		debug!("[PermissionValidator] Permission registered: {}", PermissionName);
392		Ok(())
393	}
394
395	/// Get all permissions for a specific role
396	///
397	/// ## Parameters
398	/// - `RoleName`: Name of the role to query
399	///
400	/// ## Returns
401	/// Vector of permission strings for the role, empty if role not found
402	pub async fn GetRolePermissions(&self, RoleName:&str) -> Vec<String> {
403		let roles = self.Roles.read().await;
404		roles.get(RoleName).map(|role| role.Permissions.clone()).unwrap_or_default()
405	}
406
407	/// Check if a user has a specific permission
408	///
409	/// ## Parameters
410	/// - `Context`: Security context for the user
411	/// - `PermissionName`: Permission name to check
412	///
413	/// /// Returns
414	/// true if user has permission, false otherwise
415	pub async fn HasPermission(&self, Context:&SecurityContext, PermissionName:&str) -> bool {
416		// Check direct permissions
417		if Context.Permissions.contains(&PermissionName.to_string()) {
418			return true;
419		}
420
421		// Check role permissions
422		let roles = self.Roles.read().await;
423		for RoleName in &Context.Roles {
424			if let Some(role) = roles.get(RoleName) {
425				if role.Permissions.contains(&PermissionName.to_string()) {
426					return true;
427				}
428			}
429		}
430
431		false
432	}
433
434	/// Initialize default roles and permissions
435	///
436	/// ## Returns
437	/// Result indicating success or error
438	///
439	/// ## Default Roles
440	/// - `user`: Read-only access to files, config, storage
441	/// - `developer`: Read/write access to files and storage
442	/// - `admin`: Full access including system operations
443	pub async fn InitializeDefaults(&self) -> Result<(), String> {
444		debug!("[PermissionValidator] Initializing default roles and permissions");
445
446		// Define default permissions
447		let DefaultPermissions = vec![
448			Permission {
449				Name:"file.read".to_string(),
450				Description:"Read file operations".to_string(),
451				Category:"file".to_string(),
452				IsSensitive:false,
453			},
454			Permission {
455				Name:"file.write".to_string(),
456				Description:"Write file operations".to_string(),
457				Category:"file".to_string(),
458				IsSensitive:false,
459			},
460			Permission {
461				Name:"config.read".to_string(),
462				Description:"Read configuration".to_string(),
463				Category:"config".to_string(),
464				IsSensitive:false,
465			},
466			Permission {
467				Name:"config.update".to_string(),
468				Description:"Update configuration".to_string(),
469				Category:"config".to_string(),
470				IsSensitive:false,
471			},
472			Permission {
473				Name:"storage.read".to_string(),
474				Description:"Read storage".to_string(),
475				Category:"storage".to_string(),
476				IsSensitive:false,
477			},
478			Permission {
479				Name:"storage.write".to_string(),
480				Description:"Write storage".to_string(),
481				Category:"storage".to_string(),
482				IsSensitive:false,
483			},
484			Permission {
485				Name:"system.external".to_string(),
486				Description:"Access external system resources".to_string(),
487				Category:"system".to_string(),
488				IsSensitive:true,
489			},
490			Permission {
491				Name:"system.execute".to_string(),
492				Description:"Execute system commands".to_string(),
493				Category:"system".to_string(),
494				IsSensitive:true,
495			},
496			Permission {
497				Name:"admin.manage".to_string(),
498				Description:"Administrative management operations".to_string(),
499				Category:"admin".to_string(),
500				IsSensitive:true,
501			},
502		];
503
504		// Register permissions
505		for Permission in DefaultPermissions {
506			self.RegisterPermission(Permission).await?;
507		}
508
509		// Define default roles
510		let DefaultRoles = vec![
511			Role {
512				Name:"user".to_string(),
513				Permissions:vec!["file.read".to_string(), "config.read".to_string(), "storage.read".to_string()],
514				Description:"Standard user with read access".to_string(),
515				ParentRole:None,
516				Priority:0,
517			},
518			Role {
519				Name:"developer".to_string(),
520				Permissions:vec![
521					"file.read".to_string(),
522					"file.write".to_string(),
523					"config.read".to_string(),
524					"storage.read".to_string(),
525					"storage.write".to_string(),
526				],
527				Description:"Developer with read/write access".to_string(),
528				ParentRole:None,
529				Priority:1,
530			},
531			Role {
532				Name:"admin".to_string(),
533				Permissions:vec![
534					"file.read".to_string(),
535					"file.write".to_string(),
536					"config.read".to_string(),
537					"config.update".to_string(),
538					"storage.read".to_string(),
539					"storage.write".to_string(),
540					"system.external".to_string(),
541					"system.execute".to_string(),
542					"admin.manage".to_string(),
543				],
544				Description:"Administrator with full access".to_string(),
545				ParentRole:None,
546				Priority:2,
547			},
548		];
549
550		// Register roles
551		for Role in DefaultRoles {
552			self.RegisterRole(Role).await?;
553		}
554
555		debug!("[PermissionValidator] Default roles and permissions initialized successfully");
556		Ok(())
557	}
558}
559
560#[cfg(test)]
561mod Tests {
562	use super::*;
563
564	#[tokio::test]
565	async fn TestCreateSecurityContext() {
566		let context = PermissionValidator::CreateSecurityContext(
567			"test-user".to_string(),
568			vec!["user".to_string(), "admin".to_string()],
569			"192.168.1.1".to_string(),
570			vec!["custom.permission".to_string()],
571		);
572
573		assert_eq!(context.UserId, "test-user".to_string());
574		assert_eq!(context.Roles, vec!["user".to_string(), "admin".to_string()]);
575		assert_eq!(context.IpAddress, "192.168.1.1".to_string());
576		assert_eq!(context.Permissions, vec!["custom.permission".to_string()]);
577	}
578
579	#[tokio::test]
580	async fn TestCreateSecurityContextDefaults() {
581		let context =
582			PermissionValidator::CreateSecurityContext("test-user".to_string(), vec![], "".to_string(), vec![]);
583
584		assert_eq!(context.UserId, "test-user".to_string());
585		assert_eq!(context.Roles, vec!["user".to_string()]);
586		assert_eq!(context.IpAddress, "127.0.0.1".to_string());
587	}
588
589	#[tokio::test]
590	async fn TestValidatePermissionNoPermissionsRequired() {
591		let validator = PermissionValidator::New(1000);
592		let context = SecurityContext {
593			UserId:"test-user".to_string(),
594			Roles:vec!["user".to_string()],
595			Permissions:vec![],
596			IpAddress:"127.0.0.1".to_string(),
597			Timestamp:SystemTime::now(),
598		};
599
600		let result = validator.ValidatePermission("unknown:operation", &context).await;
601		assert!(result.is_ok(), "Should succeed when no permissions are required");
602	}
603
604	#[tokio::test]
605	async fn TestValidatePermissionMissingPermission() {
606		let validator = PermissionValidator::New(1000);
607		let context = SecurityContext {
608			UserId:"test-user".to_string(),
609			Roles:vec!["user".to_string()],
610			Permissions:vec![],
611			IpAddress:"127.0.0.1".to_string(),
612			Timestamp:SystemTime::now(),
613		};
614
615		let result = validator.ValidatePermission("file:write", &context).await;
616		assert!(result.is_err(), "Should fail without required permission");
617		assert!(result.unwrap_err().contains("Missing required permission"));
618	}
619
620	#[tokio::test]
621	async fn TestValidatePermissionWithDirectPermission() {
622		let validator = PermissionValidator::New(1000);
623		let context = SecurityContext {
624			UserId:"test-user".to_string(),
625			Roles:vec![],
626			Permissions:vec!["file.write".to_string()],
627			IpAddress:"127.0.0.1".to_string(),
628			Timestamp:SystemTime::now(),
629		};
630
631		let result = validator.ValidatePermission("file:write", &context).await;
632		assert!(result.is_ok(), "Should succeed with direct permission");
633	}
634
635	#[tokio::test]
636	async fn TestValidatePermissionViaRole() {
637		let validator = PermissionValidator::New(1000);
638		validator.InitializeDefaults().await.unwrap();
639
640		let context = SecurityContext {
641			UserId:"test-user".to_string(),
642			Roles:vec!["admin".to_string()],
643			Permissions:vec![],
644			IpAddress:"127.0.0.1".to_string(),
645			Timestamp:SystemTime::now(),
646		};
647
648		let result = validator.ValidatePermission("file:write", &context).await;
649		assert!(result.is_ok(), "Should succeed via role permission");
650	}
651
652	#[tokio::test]
653	async fn TestValidatePermissionEmptyOperation() {
654		let validator = PermissionValidator::New(1000);
655		let context = SecurityContext {
656			UserId:"test-user".to_string(),
657			Roles:vec!["user".to_string()],
658			Permissions:vec![],
659			IpAddress:"127.0.0.1".to_string(),
660			Timestamp:SystemTime::now(),
661		};
662
663		let result = validator.ValidatePermission("", &context).await;
664		assert!(result.is_err(), "Should fail with empty operation name");
665	}
666
667	#[tokio::test]
668	async fn TestValidatePermissionEmptyUserId() {
669		let validator = PermissionValidator::New(1000);
670		let context = SecurityContext {
671			UserId:"".to_string(),
672			Roles:vec!["user".to_string()],
673			Permissions:vec![],
674			IpAddress:"127.0.0.1".to_string(),
675			Timestamp:SystemTime::now(),
676		};
677
678		let result = validator.ValidatePermission("file:read", &context).await;
679		assert!(result.is_err(), "Should fail with empty user ID");
680	}
681
682	#[tokio::test]
683	async fn TestInitializeDefaults() {
684		let validator = PermissionValidator::New(1000);
685		let result = validator.InitializeDefaults().await;
686		assert!(result.is_ok(), "Should initialize defaults successfully");
687
688		// Check roles exist
689		let user_perms = validator.GetRolePermissions("user").await;
690		assert!(user_perms.contains(&"file.read".to_string()));
691
692		let admin_perms = validator.GetRolePermissions("admin").await;
693		assert_ne!(admin_perms.len(), 0, "Admin should have many permissions");
694	}
695
696	#[tokio::test]
697	async fn TestGetRolePermissions() {
698		let validator = PermissionValidator::New(1000);
699		validator.InitializeDefaults().await.unwrap();
700
701		let role_perms = validator.GetRolePermissions("developer").await;
702		assert!(role_perms.contains(&"file.read".to_string()));
703		assert!(role_perms.contains(&"file.write".to_string()));
704	}
705
706	#[tokio::test]
707	async fn TestHasPermissionWithRole() {
708		let validator = PermissionValidator::New(1000);
709		validator.InitializeDefaults().await.unwrap();
710
711		let context = SecurityContext {
712			UserId:"test-user".to_string(),
713			Roles:vec!["admin".to_string()],
714			Permissions:vec![],
715			IpAddress:"127.0.0.1".to_string(),
716			Timestamp:SystemTime::now(),
717		};
718
719		assert!(validator.HasPermission(&context, "file.read").await);
720		assert!(validator.HasPermission(&context, "config.update").await);
721	}
722
723	#[tokio::test]
724	async fn TestHasPermissionDirect() {
725		let validator = PermissionValidator::New(1000);
726
727		let context = SecurityContext {
728			UserId:"test-user".to_string(),
729			Roles:vec![],
730			Permissions:vec!["custom.perm".to_string()],
731			IpAddress:"127.0.0.1".to_string(),
732			Timestamp:SystemTime::now(),
733		};
734
735		assert!(validator.HasPermission(&context, "custom.perm").await);
736		assert!(!validator.HasPermission(&context, "file.read").await);
737	}
738}