code_mesh_core/tool/
permission.rs1use 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#[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#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
24pub enum RiskLevel {
25 Low, Medium, High, Critical, }
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub enum PermissionResult {
34 Granted,
35 Denied(String),
36 RequiresConfirmation(PermissionRequest),
37}
38
39#[async_trait]
41pub trait PermissionProvider: Send + Sync {
42 async fn check_permission(&self, request: &PermissionRequest) -> PermissionResult;
44
45 async fn grant_permission(&self, request_id: &str) -> Result<(), String>;
47
48 async fn deny_permission(&self, request_id: &str, reason: String) -> Result<(), String>;
50}
51
52pub 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 if self.auto_approve_low_risk && request.risk_level == RiskLevel::Low {
72 return PermissionResult::Granted;
73 }
74
75 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
95pub 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
113pub 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 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 Err(crate::error::Error::Other(anyhow::anyhow!(
137 "Operation requires user confirmation: {}",
138 request.title
139 )))
140 }
141 }
142 }
143}
144
145pub 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}