1use 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
27pub struct IntegratedPermissionManager {
34 tool_permission_manager: ToolPermissionManager,
36 legacy_permission_manager: Option<Arc<Mutex<PermissionManager>>>,
38 legacy_permission_store: Option<Arc<Mutex<ToolPermissionStore>>>,
40 check_legacy_first: bool,
42}
43
44impl IntegratedPermissionManager {
45 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 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 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 pub fn set_check_legacy_first(&mut self, check_first: bool) {
108 self.check_legacy_first = check_first;
109 }
110
111 pub fn tool_permission_manager(&self) -> &ToolPermissionManager {
113 &self.tool_permission_manager
114 }
115
116 pub fn tool_permission_manager_mut(&mut self) -> &mut ToolPermissionManager {
118 &mut self.tool_permission_manager
119 }
120
121 pub async fn is_allowed(
138 &self,
139 tool: &str,
140 params: &HashMap<String, Value>,
141 context: &PermissionContext,
142 ) -> PermissionResult {
143 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 self.tool_permission_manager
152 .is_allowed(tool, params, context)
153 }
154
155 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 if let Some(level) = manager.get_user_permission(tool) {
171 return Some(Self::permission_level_to_result(level, tool, "user"));
172 }
173
174 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 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 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 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 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 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 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 self.tool_permission_manager
327 .is_allowed(tool, params, context)
328 }
329
330 pub fn set_legacy_store(&mut self, store: Arc<Mutex<ToolPermissionStore>>) {
339 self.legacy_permission_store = Some(store);
340 }
341
342 pub fn set_legacy_manager(&mut self, manager: Arc<Mutex<PermissionManager>>) {
346 self.legacy_permission_manager = Some(manager);
347 }
348
349 pub fn has_legacy_systems(&self) -> bool {
351 self.legacy_permission_manager.is_some() || self.legacy_permission_store.is_some()
352 }
353
354 pub fn legacy_permission_manager(&self) -> Option<&Arc<Mutex<PermissionManager>>> {
356 self.legacy_permission_manager.as_ref()
357 }
358
359 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
371pub 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
431pub 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 Permission::DenyOnce
455 }
456}
457
458pub fn permission_level_to_permission(level: PermissionLevel) -> Permission {
468 match level {
469 PermissionLevel::AlwaysAllow => Permission::AlwaysAllow,
470 PermissionLevel::AskBefore => Permission::DenyOnce, PermissionLevel::NeverAllow => Permission::DenyOnce,
472 }
473}
474
475pub fn permission_to_permission_level(permission: &Permission) -> PermissionLevel {
485 match permission {
486 Permission::AlwaysAllow => PermissionLevel::AlwaysAllow,
487 Permission::AllowOnce => PermissionLevel::AskBefore, Permission::Cancel | Permission::DenyOnce => PermissionLevel::AskBefore, }
490}
491
492pub fn is_permission_allowed(permission: &Permission) -> bool {
502 matches!(permission, Permission::AlwaysAllow | Permission::AllowOnce)
503}
504
505pub fn is_permission_permanent(permission: &Permission) -> bool {
515 matches!(permission, Permission::AlwaysAllow)
516}
517
518pub 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
536pub 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#[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}