code_mesh_core/tool/
permission.rs

1//! Permission system for tool execution
2//! Provides user confirmation prompts and access control for destructive operations
3
4use async_trait::async_trait;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::HashMap;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11/// Permission request for destructive operations
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct PermissionRequest {
14    pub id: String,
15    pub session_id: String,
16    pub title: String,
17    pub description: Option<String>,
18    pub metadata: Value,
19    pub risk_level: RiskLevel,
20}
21
22/// Risk levels for different operations
23#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
24pub enum RiskLevel {
25    Low,      // Read operations, safe writes
26    Medium,   // File modifications, process execution
27    High,     // System modifications, dangerous commands
28    Critical, // Irreversible operations
29}
30
31/// Result of a permission check
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub enum PermissionResult {
34    Granted,
35    Denied(String),
36    RequiresConfirmation(PermissionRequest),
37}
38
39/// Permission provider trait
40#[async_trait]
41pub trait PermissionProvider: Send + Sync {
42    /// Check if an operation is permitted
43    async fn check_permission(&self, request: &PermissionRequest) -> PermissionResult;
44    
45    /// Grant permission for a specific request
46    async fn grant_permission(&self, request_id: &str) -> Result<(), String>;
47    
48    /// Deny permission for a specific request
49    async fn deny_permission(&self, request_id: &str, reason: String) -> Result<(), String>;
50}
51
52/// Interactive permission provider that prompts users
53pub struct InteractivePermissionProvider {
54    pending_requests: Arc<RwLock<HashMap<String, PermissionRequest>>>,
55    auto_approve_low_risk: bool,
56}
57
58impl InteractivePermissionProvider {
59    pub fn new(auto_approve_low_risk: bool) -> Self {
60        Self {
61            pending_requests: Arc::new(RwLock::new(HashMap::new())),
62            auto_approve_low_risk,
63        }
64    }
65}
66
67#[async_trait]
68impl PermissionProvider for InteractivePermissionProvider {
69    async fn check_permission(&self, request: &PermissionRequest) -> PermissionResult {
70        // Auto-approve low-risk operations if configured
71        if self.auto_approve_low_risk && request.risk_level == RiskLevel::Low {
72            return PermissionResult::Granted;
73        }
74        
75        // For higher risk operations, require explicit confirmation
76        let mut pending = self.pending_requests.write().await;
77        pending.insert(request.id.clone(), request.clone());
78        
79        PermissionResult::RequiresConfirmation(request.clone())
80    }
81    
82    async fn grant_permission(&self, request_id: &str) -> Result<(), String> {
83        let mut pending = self.pending_requests.write().await;
84        pending.remove(request_id);
85        Ok(())
86    }
87    
88    async fn deny_permission(&self, request_id: &str, reason: String) -> Result<(), String> {
89        let mut pending = self.pending_requests.write().await;
90        pending.remove(request_id);
91        Err(reason)
92    }
93}
94
95/// Auto-approve permission provider for testing
96pub struct AutoApprovePermissionProvider;
97
98#[async_trait]
99impl PermissionProvider for AutoApprovePermissionProvider {
100    async fn check_permission(&self, _request: &PermissionRequest) -> PermissionResult {
101        PermissionResult::Granted
102    }
103    
104    async fn grant_permission(&self, _request_id: &str) -> Result<(), String> {
105        Ok(())
106    }
107    
108    async fn deny_permission(&self, _request_id: &str, reason: String) -> Result<(), String> {
109        Err(reason)
110    }
111}
112
113/// Global permission manager
114pub struct PermissionManager {
115    provider: Box<dyn PermissionProvider>,
116}
117
118impl PermissionManager {
119    pub fn new(provider: Box<dyn PermissionProvider>) -> Self {
120        Self { provider }
121    }
122    
123    /// Ask for permission to perform an operation
124    pub async fn ask(&self, request: PermissionRequest) -> Result<(), crate::error::Error> {
125        match self.provider.check_permission(&request).await {
126            PermissionResult::Granted => Ok(()),
127            PermissionResult::Denied(reason) => {
128                Err(crate::error::Error::Other(anyhow::anyhow!(
129                    "Permission denied: {}",
130                    reason
131                )))
132            }
133            PermissionResult::RequiresConfirmation(_) => {
134                // In a real implementation, this would trigger a UI prompt
135                // For now, we'll auto-deny operations requiring confirmation
136                Err(crate::error::Error::Other(anyhow::anyhow!(
137                    "Operation requires user confirmation: {}",
138                    request.title
139                )))
140            }
141        }
142    }
143}
144
145/// Helper function to create permission requests
146pub fn create_permission_request(
147    id: impl Into<String>,
148    session_id: impl Into<String>,
149    title: impl Into<String>,
150    risk_level: RiskLevel,
151    metadata: Value,
152) -> PermissionRequest {
153    PermissionRequest {
154        id: id.into(),
155        session_id: session_id.into(),
156        title: title.into(),
157        description: None,
158        metadata,
159        risk_level,
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use serde_json::json;
167    
168    #[tokio::test]
169    async fn test_auto_approve_low_risk() {
170        let provider = InteractivePermissionProvider::new(true);
171        let request = create_permission_request(
172            "test",
173            "session1",
174            "Read file",
175            RiskLevel::Low,
176            json!({}),
177        );
178        
179        let result = provider.check_permission(&request).await;
180        assert!(matches!(result, PermissionResult::Granted));
181    }
182    
183    #[tokio::test]
184    async fn test_requires_confirmation_high_risk() {
185        let provider = InteractivePermissionProvider::new(true);
186        let request = create_permission_request(
187            "test",
188            "session1",
189            "Delete system files",
190            RiskLevel::Critical,
191            json!({}),
192        );
193        
194        let result = provider.check_permission(&request).await;
195        assert!(matches!(result, PermissionResult::RequiresConfirmation(_)));
196    }
197    
198    #[tokio::test]
199    async fn test_auto_approve_provider() {
200        let provider = AutoApprovePermissionProvider;
201        let request = create_permission_request(
202            "test",
203            "session1",
204            "Any operation",
205            RiskLevel::Critical,
206            json!({}),
207        );
208        
209        let result = provider.check_permission(&request).await;
210        assert!(matches!(result, PermissionResult::Granted));
211    }
212}