Skip to main content

aster/permission/
integration.rs

1//! Integration Module for Tool Permission System
2//!
3//! This module provides integration between the new `ToolPermissionManager` and
4//! the existing permission infrastructure in Aster.
5//!
6//! Features:
7//! - Integration with existing `PermissionManager` for user-defined permissions
8//! - Integration with existing `ToolPermissionStore` for permission persistence
9//! - Support for existing `Permission` enum (AlwaysAllow, AllowOnce, Cancel, DenyOnce)
10//! - Backward-compatible integration with tool_execution module
11//!
12//! Requirements: 11.1, 11.2, 11.3, 11.4, 11.5
13
14use super::manager::ToolPermissionManager;
15use super::permission_confirmation::Permission;
16use super::permission_store::ToolPermissionStore;
17use super::types::{PermissionContext, PermissionResult, PermissionScope, ToolPermission};
18use crate::config::permission::PermissionLevel;
19use crate::config::PermissionManager;
20use crate::conversation::message::ToolRequest;
21use serde_json::Value;
22use std::collections::HashMap;
23use std::path::PathBuf;
24use std::sync::Arc;
25use tokio::sync::Mutex;
26
27/// Integrated Permission Manager
28///
29/// Combines the new `ToolPermissionManager` with the existing `PermissionManager`
30/// and `ToolPermissionStore` to provide a unified permission checking interface.
31///
32/// Requirements: 11.1, 11.2
33pub struct IntegratedPermissionManager {
34    /// The new tool permission manager with advanced features
35    tool_permission_manager: ToolPermissionManager,
36    /// Reference to the existing permission manager for user-defined permissions
37    legacy_permission_manager: Option<Arc<Mutex<PermissionManager>>>,
38    /// Reference to the existing tool permission store for persistence
39    legacy_permission_store: Option<Arc<Mutex<ToolPermissionStore>>>,
40    /// Whether to check legacy systems first
41    check_legacy_first: bool,
42}
43
44impl IntegratedPermissionManager {
45    /// Create a new IntegratedPermissionManager
46    ///
47    /// # Arguments
48    /// * `config_dir` - Optional configuration directory for the new permission system
49    ///
50    /// # Returns
51    /// A new IntegratedPermissionManager instance
52    ///
53    /// Requirements: 11.1
54    pub fn new(config_dir: Option<PathBuf>) -> Self {
55        Self {
56            tool_permission_manager: ToolPermissionManager::new(config_dir),
57            legacy_permission_manager: None,
58            legacy_permission_store: None,
59            check_legacy_first: true,
60        }
61    }
62
63    /// Create with existing PermissionManager
64    ///
65    /// # Arguments
66    /// * `config_dir` - Optional configuration directory
67    /// * `legacy_manager` - Reference to existing PermissionManager
68    ///
69    /// Requirements: 11.1
70    pub fn with_legacy_manager(
71        config_dir: Option<PathBuf>,
72        legacy_manager: Arc<Mutex<PermissionManager>>,
73    ) -> Self {
74        Self {
75            tool_permission_manager: ToolPermissionManager::new(config_dir),
76            legacy_permission_manager: Some(legacy_manager),
77            legacy_permission_store: None,
78            check_legacy_first: true,
79        }
80    }
81
82    /// Create with existing PermissionManager and ToolPermissionStore
83    ///
84    /// # Arguments
85    /// * `config_dir` - Optional configuration directory
86    /// * `legacy_manager` - Reference to existing PermissionManager
87    /// * `legacy_store` - Reference to existing ToolPermissionStore
88    ///
89    /// Requirements: 11.1, 11.2
90    pub fn with_legacy_systems(
91        config_dir: Option<PathBuf>,
92        legacy_manager: Arc<Mutex<PermissionManager>>,
93        legacy_store: Arc<Mutex<ToolPermissionStore>>,
94    ) -> Self {
95        Self {
96            tool_permission_manager: ToolPermissionManager::new(config_dir),
97            legacy_permission_manager: Some(legacy_manager),
98            legacy_permission_store: Some(legacy_store),
99            check_legacy_first: true,
100        }
101    }
102
103    /// Set whether to check legacy systems first
104    ///
105    /// When true (default), the legacy PermissionManager is checked before
106    /// the new ToolPermissionManager. This ensures backward compatibility.
107    pub fn set_check_legacy_first(&mut self, check_first: bool) {
108        self.check_legacy_first = check_first;
109    }
110
111    /// Get a reference to the underlying ToolPermissionManager
112    pub fn tool_permission_manager(&self) -> &ToolPermissionManager {
113        &self.tool_permission_manager
114    }
115
116    /// Get a mutable reference to the underlying ToolPermissionManager
117    pub fn tool_permission_manager_mut(&mut self) -> &mut ToolPermissionManager {
118        &mut self.tool_permission_manager
119    }
120
121    /// Check if a tool is allowed to execute
122    ///
123    /// This method integrates both the legacy and new permission systems:
124    /// 1. If check_legacy_first is true, check the legacy PermissionManager first
125    /// 2. If legacy returns a definitive answer (AlwaysAllow or NeverAllow), use it
126    /// 3. Otherwise, fall back to the new ToolPermissionManager
127    ///
128    /// # Arguments
129    /// * `tool` - The tool name to check
130    /// * `params` - The tool parameters
131    /// * `context` - The permission context
132    ///
133    /// # Returns
134    /// A PermissionResult containing the decision and details
135    ///
136    /// Requirements: 11.1
137    pub async fn is_allowed(
138        &self,
139        tool: &str,
140        params: &HashMap<String, Value>,
141        context: &PermissionContext,
142    ) -> PermissionResult {
143        // Check legacy system first if configured
144        if self.check_legacy_first {
145            if let Some(legacy_result) = self.check_legacy_permission(tool).await {
146                return legacy_result;
147            }
148        }
149
150        // Fall back to new permission system
151        self.tool_permission_manager
152            .is_allowed(tool, params, context)
153    }
154
155    /// Check permission using the legacy PermissionManager
156    ///
157    /// # Arguments
158    /// * `tool` - The tool name to check
159    ///
160    /// # Returns
161    /// Some(PermissionResult) if the legacy system has a definitive answer,
162    /// None if the new system should be consulted
163    ///
164    /// Requirements: 11.1
165    async fn check_legacy_permission(&self, tool: &str) -> Option<PermissionResult> {
166        let legacy_manager = self.legacy_permission_manager.as_ref()?;
167        let manager = legacy_manager.lock().await;
168
169        // Check user-defined permission
170        if let Some(level) = manager.get_user_permission(tool) {
171            return Some(Self::permission_level_to_result(level, tool, "user"));
172        }
173
174        // Check smart approve permission
175        if let Some(level) = manager.get_smart_approve_permission(tool) {
176            return Some(Self::permission_level_to_result(
177                level,
178                tool,
179                "smart_approve",
180            ));
181        }
182
183        None
184    }
185
186    /// Convert PermissionLevel to PermissionResult
187    ///
188    /// # Arguments
189    /// * `level` - The permission level from the legacy system
190    /// * `tool` - The tool name
191    /// * `source` - The source of the permission (e.g., "user", "smart_approve")
192    ///
193    /// # Returns
194    /// A PermissionResult based on the permission level
195    fn permission_level_to_result(
196        level: PermissionLevel,
197        tool: &str,
198        source: &str,
199    ) -> PermissionResult {
200        match level {
201            PermissionLevel::AlwaysAllow => PermissionResult {
202                allowed: true,
203                reason: Some(format!(
204                    "Tool '{}' is always allowed by {} permission",
205                    tool, source
206                )),
207                restricted: false,
208                suggestions: Vec::new(),
209                matched_rule: None,
210                violations: Vec::new(),
211            },
212            PermissionLevel::NeverAllow => PermissionResult {
213                allowed: false,
214                reason: Some(format!(
215                    "Tool '{}' is never allowed by {} permission",
216                    tool, source
217                )),
218                restricted: false,
219                suggestions: vec![
220                    "This tool is blocked by user configuration.".to_string(),
221                    "Update permission settings to allow this tool.".to_string(),
222                ],
223                matched_rule: None,
224                violations: Vec::new(),
225            },
226            PermissionLevel::AskBefore => PermissionResult {
227                allowed: false,
228                reason: Some(format!(
229                    "Tool '{}' requires approval by {} permission",
230                    tool, source
231                )),
232                restricted: false,
233                suggestions: vec!["This tool requires user approval before execution.".to_string()],
234                matched_rule: None,
235                violations: Vec::new(),
236            },
237        }
238    }
239
240    /// Check permission for a tool request using the legacy store
241    ///
242    /// # Arguments
243    /// * `tool_request` - The tool request to check
244    ///
245    /// # Returns
246    /// Some(bool) if the legacy store has a cached decision, None otherwise
247    ///
248    /// Requirements: 11.2
249    pub async fn check_legacy_store(&self, tool_request: &ToolRequest) -> Option<bool> {
250        let legacy_store = self.legacy_permission_store.as_ref()?;
251        let store = legacy_store.lock().await;
252        store.check_permission(tool_request)
253    }
254
255    /// Record a permission decision in the legacy store
256    ///
257    /// # Arguments
258    /// * `tool_request` - The tool request
259    /// * `allowed` - Whether the tool was allowed
260    /// * `expiry_duration` - Optional expiry duration
261    ///
262    /// Requirements: 11.2
263    pub async fn record_legacy_permission(
264        &self,
265        tool_request: &ToolRequest,
266        allowed: bool,
267        expiry_duration: Option<std::time::Duration>,
268    ) -> anyhow::Result<()> {
269        if let Some(legacy_store) = &self.legacy_permission_store {
270            let mut store = legacy_store.lock().await;
271            store.record_permission(tool_request, allowed, expiry_duration)?;
272        }
273        Ok(())
274    }
275
276    /// Check permission using both legacy store and new system
277    ///
278    /// This method provides a comprehensive permission check that:
279    /// 1. Checks the legacy ToolPermissionStore for cached decisions
280    /// 2. Falls back to the legacy PermissionManager
281    /// 3. Finally checks the new ToolPermissionManager
282    ///
283    /// # Arguments
284    /// * `tool` - The tool name
285    /// * `tool_request` - Optional tool request for store lookup
286    /// * `params` - The tool parameters
287    /// * `context` - The permission context
288    ///
289    /// # Returns
290    /// A PermissionResult containing the decision and details
291    ///
292    /// Requirements: 11.1, 11.2
293    pub async fn check_permission_comprehensive(
294        &self,
295        tool: &str,
296        tool_request: Option<&ToolRequest>,
297        params: &HashMap<String, Value>,
298        context: &PermissionContext,
299    ) -> PermissionResult {
300        // 1. Check legacy store first if we have a tool request
301        if let Some(request) = tool_request {
302            if let Some(allowed) = self.check_legacy_store(request).await {
303                return PermissionResult {
304                    allowed,
305                    reason: Some(format!(
306                        "Tool '{}' {} by cached permission",
307                        tool,
308                        if allowed { "allowed" } else { "denied" }
309                    )),
310                    restricted: false,
311                    suggestions: Vec::new(),
312                    matched_rule: None,
313                    violations: Vec::new(),
314                };
315            }
316        }
317
318        // 2. Check legacy PermissionManager
319        if self.check_legacy_first {
320            if let Some(legacy_result) = self.check_legacy_permission(tool).await {
321                return legacy_result;
322            }
323        }
324
325        // 3. Fall back to new permission system
326        self.tool_permission_manager
327            .is_allowed(tool, params, context)
328    }
329
330    /// Sync permissions from legacy store to new system
331    ///
332    /// This method is useful for migration scenarios where you want to
333    /// import existing cached permissions into the new system.
334    ///
335    /// Note: This is a one-way sync and doesn't modify the legacy store.
336    ///
337    /// Requirements: 11.2, 11.5
338    pub fn set_legacy_store(&mut self, store: Arc<Mutex<ToolPermissionStore>>) {
339        self.legacy_permission_store = Some(store);
340    }
341
342    /// Set the legacy permission manager
343    ///
344    /// Requirements: 11.1
345    pub fn set_legacy_manager(&mut self, manager: Arc<Mutex<PermissionManager>>) {
346        self.legacy_permission_manager = Some(manager);
347    }
348
349    /// Check if legacy systems are configured
350    pub fn has_legacy_systems(&self) -> bool {
351        self.legacy_permission_manager.is_some() || self.legacy_permission_store.is_some()
352    }
353
354    /// Get reference to legacy permission manager if set
355    pub fn legacy_permission_manager(&self) -> Option<&Arc<Mutex<PermissionManager>>> {
356        self.legacy_permission_manager.as_ref()
357    }
358
359    /// Get reference to legacy permission store if set
360    pub fn legacy_permission_store(&self) -> Option<&Arc<Mutex<ToolPermissionStore>>> {
361        self.legacy_permission_store.as_ref()
362    }
363}
364
365impl Default for IntegratedPermissionManager {
366    fn default() -> Self {
367        Self::new(None)
368    }
369}
370
371// ============================================================================
372// Permission Enum Conversion
373// ============================================================================
374
375/// Convert the existing Permission enum to a PermissionResult
376///
377/// This function provides compatibility with the existing Permission enum
378/// (AlwaysAllow, AllowOnce, Cancel, DenyOnce) used in the tool_execution module.
379///
380/// # Arguments
381/// * `permission` - The existing Permission enum value
382/// * `tool` - The tool name for context
383///
384/// # Returns
385/// A PermissionResult that represents the same decision
386///
387/// Requirements: 11.3
388pub fn permission_to_result(permission: &Permission, tool: &str) -> PermissionResult {
389    match permission {
390        Permission::AlwaysAllow => PermissionResult {
391            allowed: true,
392            reason: Some(format!("Tool '{}' is always allowed", tool)),
393            restricted: false,
394            suggestions: Vec::new(),
395            matched_rule: None,
396            violations: Vec::new(),
397        },
398        Permission::AllowOnce => PermissionResult {
399            allowed: true,
400            reason: Some(format!("Tool '{}' is allowed for this execution", tool)),
401            restricted: false,
402            suggestions: Vec::new(),
403            matched_rule: None,
404            violations: Vec::new(),
405        },
406        Permission::Cancel => PermissionResult {
407            allowed: false,
408            reason: Some(format!("Tool '{}' execution was cancelled", tool)),
409            restricted: false,
410            suggestions: vec![
411                "The user cancelled this tool execution.".to_string(),
412                "Try a different approach or ask for clarification.".to_string(),
413            ],
414            matched_rule: None,
415            violations: Vec::new(),
416        },
417        Permission::DenyOnce => PermissionResult {
418            allowed: false,
419            reason: Some(format!("Tool '{}' is denied for this execution", tool)),
420            restricted: false,
421            suggestions: vec![
422                "The user denied this specific tool execution.".to_string(),
423                "You may try again with different parameters.".to_string(),
424            ],
425            matched_rule: None,
426            violations: Vec::new(),
427        },
428    }
429}
430
431/// Convert a PermissionResult to the existing Permission enum
432///
433/// This function provides reverse compatibility, converting the new
434/// PermissionResult back to the existing Permission enum.
435///
436/// # Arguments
437/// * `result` - The PermissionResult to convert
438/// * `is_permanent` - Whether the decision should be permanent (AlwaysAllow vs AllowOnce)
439///
440/// # Returns
441/// The corresponding Permission enum value
442///
443/// Requirements: 11.3
444pub fn result_to_permission(result: &PermissionResult, is_permanent: bool) -> Permission {
445    if result.allowed {
446        if is_permanent {
447            Permission::AlwaysAllow
448        } else {
449            Permission::AllowOnce
450        }
451    } else {
452        // For denials, we use DenyOnce as the default
453        // Cancel is typically used for user-initiated cancellation
454        Permission::DenyOnce
455    }
456}
457
458/// Convert PermissionLevel to Permission enum
459///
460/// # Arguments
461/// * `level` - The PermissionLevel from the config system
462///
463/// # Returns
464/// The corresponding Permission enum value
465///
466/// Requirements: 11.3
467pub fn permission_level_to_permission(level: PermissionLevel) -> Permission {
468    match level {
469        PermissionLevel::AlwaysAllow => Permission::AlwaysAllow,
470        PermissionLevel::AskBefore => Permission::DenyOnce, // Requires approval
471        PermissionLevel::NeverAllow => Permission::DenyOnce,
472    }
473}
474
475/// Convert Permission enum to PermissionLevel
476///
477/// # Arguments
478/// * `permission` - The Permission enum value
479///
480/// # Returns
481/// The corresponding PermissionLevel
482///
483/// Requirements: 11.3
484pub fn permission_to_permission_level(permission: &Permission) -> PermissionLevel {
485    match permission {
486        Permission::AlwaysAllow => PermissionLevel::AlwaysAllow,
487        Permission::AllowOnce => PermissionLevel::AskBefore, // One-time allow still needs asking next time
488        Permission::Cancel | Permission::DenyOnce => PermissionLevel::AskBefore, // Denials don't persist as NeverAllow
489    }
490}
491
492/// Check if a Permission represents an allowed action
493///
494/// # Arguments
495/// * `permission` - The Permission enum value
496///
497/// # Returns
498/// true if the permission allows the action
499///
500/// Requirements: 11.3
501pub fn is_permission_allowed(permission: &Permission) -> bool {
502    matches!(permission, Permission::AlwaysAllow | Permission::AllowOnce)
503}
504
505/// Check if a Permission is permanent (affects future executions)
506///
507/// # Arguments
508/// * `permission` - The Permission enum value
509///
510/// # Returns
511/// true if the permission is permanent
512///
513/// Requirements: 11.3
514pub fn is_permission_permanent(permission: &Permission) -> bool {
515    matches!(permission, Permission::AlwaysAllow)
516}
517
518/// Create a Permission from an allowed flag and permanence
519///
520/// # Arguments
521/// * `allowed` - Whether the action is allowed
522/// * `permanent` - Whether the decision is permanent
523///
524/// # Returns
525/// The corresponding Permission enum value
526///
527/// Requirements: 11.3
528pub fn create_permission(allowed: bool, permanent: bool) -> Permission {
529    match (allowed, permanent) {
530        (true, true) => Permission::AlwaysAllow,
531        (true, false) => Permission::AllowOnce,
532        (false, _) => Permission::DenyOnce,
533    }
534}
535
536// ============================================================================
537// ToolPermission Conversion
538// ============================================================================
539
540/// Convert a PermissionLevel to a ToolPermission
541///
542/// Creates a new ToolPermission based on the legacy PermissionLevel.
543///
544/// # Arguments
545/// * `tool` - The tool name
546/// * `level` - The permission level
547/// * `scope` - The scope for the new permission
548///
549/// # Returns
550/// A ToolPermission representing the same permission
551///
552/// Requirements: 11.5
553pub fn permission_level_to_tool_permission(
554    tool: &str,
555    level: PermissionLevel,
556    scope: PermissionScope,
557) -> ToolPermission {
558    let (allowed, reason) = match level {
559        PermissionLevel::AlwaysAllow => {
560            (true, Some("Migrated from legacy: AlwaysAllow".to_string()))
561        }
562        PermissionLevel::AskBefore => (
563            false,
564            Some("Migrated from legacy: AskBefore (requires approval)".to_string()),
565        ),
566        PermissionLevel::NeverAllow => {
567            (false, Some("Migrated from legacy: NeverAllow".to_string()))
568        }
569    };
570
571    ToolPermission {
572        tool: tool.to_string(),
573        allowed,
574        priority: 0,
575        conditions: Vec::new(),
576        parameter_restrictions: Vec::new(),
577        scope,
578        reason,
579        expires_at: None,
580        metadata: HashMap::new(),
581    }
582}
583
584// ============================================================================
585// Tests
586// ============================================================================
587
588#[cfg(test)]
589mod tests {
590    use super::*;
591
592    #[test]
593    fn test_permission_to_result_always_allow() {
594        let result = permission_to_result(&Permission::AlwaysAllow, "test_tool");
595        assert!(result.allowed);
596        assert!(result.reason.is_some());
597    }
598
599    #[test]
600    fn test_permission_to_result_allow_once() {
601        let result = permission_to_result(&Permission::AllowOnce, "test_tool");
602        assert!(result.allowed);
603    }
604
605    #[test]
606    fn test_permission_to_result_cancel() {
607        let result = permission_to_result(&Permission::Cancel, "test_tool");
608        assert!(!result.allowed);
609        assert!(!result.suggestions.is_empty());
610    }
611
612    #[test]
613    fn test_permission_to_result_deny_once() {
614        let result = permission_to_result(&Permission::DenyOnce, "test_tool");
615        assert!(!result.allowed);
616    }
617
618    #[test]
619    fn test_result_to_permission_allowed_permanent() {
620        let result = PermissionResult {
621            allowed: true,
622            reason: None,
623            restricted: false,
624            suggestions: Vec::new(),
625            matched_rule: None,
626            violations: Vec::new(),
627        };
628        assert_eq!(result_to_permission(&result, true), Permission::AlwaysAllow);
629    }
630
631    #[test]
632    fn test_result_to_permission_allowed_temporary() {
633        let result = PermissionResult {
634            allowed: true,
635            reason: None,
636            restricted: false,
637            suggestions: Vec::new(),
638            matched_rule: None,
639            violations: Vec::new(),
640        };
641        assert_eq!(result_to_permission(&result, false), Permission::AllowOnce);
642    }
643
644    #[test]
645    fn test_result_to_permission_denied() {
646        let result = PermissionResult {
647            allowed: false,
648            reason: None,
649            restricted: false,
650            suggestions: Vec::new(),
651            matched_rule: None,
652            violations: Vec::new(),
653        };
654        assert_eq!(result_to_permission(&result, false), Permission::DenyOnce);
655    }
656
657    #[test]
658    fn test_permission_level_to_permission() {
659        assert_eq!(
660            permission_level_to_permission(PermissionLevel::AlwaysAllow),
661            Permission::AlwaysAllow
662        );
663        assert_eq!(
664            permission_level_to_permission(PermissionLevel::AskBefore),
665            Permission::DenyOnce
666        );
667        assert_eq!(
668            permission_level_to_permission(PermissionLevel::NeverAllow),
669            Permission::DenyOnce
670        );
671    }
672
673    #[test]
674    fn test_permission_to_permission_level() {
675        assert_eq!(
676            permission_to_permission_level(&Permission::AlwaysAllow),
677            PermissionLevel::AlwaysAllow
678        );
679        assert_eq!(
680            permission_to_permission_level(&Permission::AllowOnce),
681            PermissionLevel::AskBefore
682        );
683        assert_eq!(
684            permission_to_permission_level(&Permission::Cancel),
685            PermissionLevel::AskBefore
686        );
687        assert_eq!(
688            permission_to_permission_level(&Permission::DenyOnce),
689            PermissionLevel::AskBefore
690        );
691    }
692
693    #[test]
694    fn test_is_permission_allowed() {
695        assert!(is_permission_allowed(&Permission::AlwaysAllow));
696        assert!(is_permission_allowed(&Permission::AllowOnce));
697        assert!(!is_permission_allowed(&Permission::Cancel));
698        assert!(!is_permission_allowed(&Permission::DenyOnce));
699    }
700
701    #[test]
702    fn test_is_permission_permanent() {
703        assert!(is_permission_permanent(&Permission::AlwaysAllow));
704        assert!(!is_permission_permanent(&Permission::AllowOnce));
705        assert!(!is_permission_permanent(&Permission::Cancel));
706        assert!(!is_permission_permanent(&Permission::DenyOnce));
707    }
708
709    #[test]
710    fn test_create_permission() {
711        assert_eq!(create_permission(true, true), Permission::AlwaysAllow);
712        assert_eq!(create_permission(true, false), Permission::AllowOnce);
713        assert_eq!(create_permission(false, true), Permission::DenyOnce);
714        assert_eq!(create_permission(false, false), Permission::DenyOnce);
715    }
716
717    #[test]
718    fn test_permission_level_to_tool_permission() {
719        let perm = permission_level_to_tool_permission(
720            "test_tool",
721            PermissionLevel::AlwaysAllow,
722            PermissionScope::Global,
723        );
724        assert_eq!(perm.tool, "test_tool");
725        assert!(perm.allowed);
726        assert_eq!(perm.scope, PermissionScope::Global);
727
728        let perm = permission_level_to_tool_permission(
729            "test_tool",
730            PermissionLevel::NeverAllow,
731            PermissionScope::Project,
732        );
733        assert!(!perm.allowed);
734        assert_eq!(perm.scope, PermissionScope::Project);
735    }
736
737    #[test]
738    fn test_integrated_permission_manager_default() {
739        let manager = IntegratedPermissionManager::default();
740        assert!(manager.legacy_permission_manager.is_none());
741        assert!(manager.legacy_permission_store.is_none());
742        assert!(manager.check_legacy_first);
743    }
744
745    #[test]
746    fn test_permission_level_to_result() {
747        let result = IntegratedPermissionManager::permission_level_to_result(
748            PermissionLevel::AlwaysAllow,
749            "test_tool",
750            "user",
751        );
752        assert!(result.allowed);
753
754        let result = IntegratedPermissionManager::permission_level_to_result(
755            PermissionLevel::NeverAllow,
756            "test_tool",
757            "user",
758        );
759        assert!(!result.allowed);
760
761        let result = IntegratedPermissionManager::permission_level_to_result(
762            PermissionLevel::AskBefore,
763            "test_tool",
764            "user",
765        );
766        assert!(!result.allowed);
767    }
768}