1use super::types::{PermissionScope, ToolPermission};
12use crate::config::permission::{PermissionConfig, PermissionLevel, PermissionManager};
13use std::collections::HashMap;
14
15#[derive(Debug, Clone, Default)]
17pub struct MigrationResult {
18 pub permissions: Vec<ToolPermission>,
20 pub warnings: Vec<String>,
22 pub always_allow_count: usize,
24 pub ask_before_count: usize,
26 pub never_allow_count: usize,
28}
29
30impl MigrationResult {
31 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn total_count(&self) -> usize {
38 self.always_allow_count + self.ask_before_count + self.never_allow_count
39 }
40}
41
42pub fn migrate_from_old_system(old_manager: &PermissionManager) -> Vec<ToolPermission> {
59 let result = migrate_from_old_system_with_details(old_manager);
60 result.permissions
61}
62
63pub fn migrate_from_old_system_with_details(old_manager: &PermissionManager) -> MigrationResult {
74 let mut result = MigrationResult::new();
75
76 let permission_names = old_manager.get_permission_names();
78
79 for name in permission_names {
80 result.warnings.push(format!(
90 "Permission category '{}' found - migration may be partial",
91 name
92 ));
93 }
94
95 result
96}
97
98pub fn migrate_permission_config(
111 config: &PermissionConfig,
112 category: &str,
113 scope: PermissionScope,
114) -> Vec<ToolPermission> {
115 let mut permissions = Vec::new();
116
117 for tool in &config.always_allow {
119 let mut metadata = HashMap::new();
120 metadata.insert(
121 "migrated_from".to_string(),
122 serde_json::Value::String("always_allow".to_string()),
123 );
124 metadata.insert(
125 "original_category".to_string(),
126 serde_json::Value::String(category.to_string()),
127 );
128
129 permissions.push(ToolPermission {
130 tool: tool.clone(),
131 allowed: true,
132 priority: 100, conditions: Vec::new(),
134 parameter_restrictions: Vec::new(),
135 scope,
136 reason: Some(format!("Migrated from {} always_allow", category)),
137 expires_at: None,
138 metadata,
139 });
140 }
141
142 for tool in &config.ask_before {
146 let mut metadata = HashMap::new();
147 metadata.insert(
148 "migrated_from".to_string(),
149 serde_json::Value::String("ask_before".to_string()),
150 );
151 metadata.insert(
152 "original_category".to_string(),
153 serde_json::Value::String(category.to_string()),
154 );
155 metadata.insert(
156 "requires_confirmation".to_string(),
157 serde_json::Value::Bool(true),
158 );
159
160 permissions.push(ToolPermission {
161 tool: tool.clone(),
162 allowed: true, priority: 50, conditions: Vec::new(),
165 parameter_restrictions: Vec::new(),
166 scope,
167 reason: Some(format!(
168 "Migrated from {} ask_before (requires confirmation)",
169 category
170 )),
171 expires_at: None,
172 metadata,
173 });
174 }
175
176 for tool in &config.never_allow {
178 let mut metadata = HashMap::new();
179 metadata.insert(
180 "migrated_from".to_string(),
181 serde_json::Value::String("never_allow".to_string()),
182 );
183 metadata.insert(
184 "original_category".to_string(),
185 serde_json::Value::String(category.to_string()),
186 );
187
188 permissions.push(ToolPermission {
189 tool: tool.clone(),
190 allowed: false,
191 priority: 100, conditions: Vec::new(),
193 parameter_restrictions: Vec::new(),
194 scope,
195 reason: Some(format!("Migrated from {} never_allow", category)),
196 expires_at: None,
197 metadata,
198 });
199 }
200
201 permissions
202}
203
204pub fn migrate_permission_level(
217 tool_name: &str,
218 level: PermissionLevel,
219 scope: PermissionScope,
220) -> ToolPermission {
221 let (allowed, priority, migrated_from) = match level {
222 PermissionLevel::AlwaysAllow => (true, 100, "always_allow"),
223 PermissionLevel::AskBefore => (true, 50, "ask_before"),
224 PermissionLevel::NeverAllow => (false, 100, "never_allow"),
225 };
226
227 let mut metadata = HashMap::new();
228 metadata.insert(
229 "migrated_from".to_string(),
230 serde_json::Value::String(migrated_from.to_string()),
231 );
232
233 let requires_confirmation = matches!(level, PermissionLevel::AskBefore);
234 if requires_confirmation {
235 metadata.insert(
236 "requires_confirmation".to_string(),
237 serde_json::Value::Bool(true),
238 );
239 }
240
241 ToolPermission {
242 tool: tool_name.to_string(),
243 allowed,
244 priority,
245 conditions: Vec::new(),
246 parameter_restrictions: Vec::new(),
247 scope,
248 reason: Some(format!("Migrated from {}", migrated_from)),
249 expires_at: None,
250 metadata,
251 }
252}
253
254pub fn migrate_known_tools(
267 old_manager: &PermissionManager,
268 tool_names: &[&str],
269 scope: PermissionScope,
270) -> MigrationResult {
271 let mut result = MigrationResult::new();
272
273 for tool_name in tool_names {
274 if let Some(level) = old_manager.get_user_permission(tool_name) {
276 let permission = migrate_permission_level(tool_name, level.clone(), scope);
277
278 match level {
279 PermissionLevel::AlwaysAllow => result.always_allow_count += 1,
280 PermissionLevel::AskBefore => result.ask_before_count += 1,
281 PermissionLevel::NeverAllow => result.never_allow_count += 1,
282 }
283
284 result.permissions.push(permission);
285 }
286
287 if let Some(level) = old_manager.get_smart_approve_permission(tool_name) {
289 let already_exists = result.permissions.iter().any(|p| p.tool == *tool_name);
291
292 if !already_exists {
293 let mut permission = migrate_permission_level(tool_name, level.clone(), scope);
294 permission.metadata.insert(
295 "original_category".to_string(),
296 serde_json::Value::String("smart_approve".to_string()),
297 );
298
299 match level {
300 PermissionLevel::AlwaysAllow => result.always_allow_count += 1,
301 PermissionLevel::AskBefore => result.ask_before_count += 1,
302 PermissionLevel::NeverAllow => result.never_allow_count += 1,
303 }
304
305 result.permissions.push(permission);
306 }
307 }
308 }
309
310 result
311}
312
313pub fn is_migrated_permission(permission: &ToolPermission) -> bool {
321 permission.metadata.contains_key("migrated_from")
322}
323
324pub fn get_original_permission_level(permission: &ToolPermission) -> Option<PermissionLevel> {
332 permission
333 .metadata
334 .get("migrated_from")
335 .and_then(|v| v.as_str())
336 .and_then(|s| match s {
337 "always_allow" => Some(PermissionLevel::AlwaysAllow),
338 "ask_before" => Some(PermissionLevel::AskBefore),
339 "never_allow" => Some(PermissionLevel::NeverAllow),
340 _ => None,
341 })
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use tempfile::NamedTempFile;
348
349 fn create_test_permission_manager() -> PermissionManager {
350 let temp_file = NamedTempFile::new().unwrap();
351 PermissionManager::new(temp_file.path())
352 }
353
354 #[test]
355 fn test_migrate_permission_level_always_allow() {
356 let permission = migrate_permission_level(
357 "test_tool",
358 PermissionLevel::AlwaysAllow,
359 PermissionScope::Global,
360 );
361
362 assert_eq!(permission.tool, "test_tool");
363 assert!(permission.allowed);
364 assert_eq!(permission.priority, 100);
365 assert_eq!(permission.scope, PermissionScope::Global);
366 assert!(permission.metadata.contains_key("migrated_from"));
367 assert_eq!(
368 permission.metadata.get("migrated_from"),
369 Some(&serde_json::Value::String("always_allow".to_string()))
370 );
371 }
372
373 #[test]
374 fn test_migrate_permission_level_ask_before() {
375 let permission = migrate_permission_level(
376 "test_tool",
377 PermissionLevel::AskBefore,
378 PermissionScope::Project,
379 );
380
381 assert_eq!(permission.tool, "test_tool");
382 assert!(permission.allowed);
383 assert_eq!(permission.priority, 50);
384 assert_eq!(permission.scope, PermissionScope::Project);
385 assert!(permission.metadata.contains_key("requires_confirmation"));
386 assert_eq!(
387 permission.metadata.get("requires_confirmation"),
388 Some(&serde_json::Value::Bool(true))
389 );
390 }
391
392 #[test]
393 fn test_migrate_permission_level_never_allow() {
394 let permission = migrate_permission_level(
395 "test_tool",
396 PermissionLevel::NeverAllow,
397 PermissionScope::Session,
398 );
399
400 assert_eq!(permission.tool, "test_tool");
401 assert!(!permission.allowed);
402 assert_eq!(permission.priority, 100);
403 assert_eq!(permission.scope, PermissionScope::Session);
404 }
405
406 #[test]
407 fn test_migrate_permission_config() {
408 let config = PermissionConfig {
409 always_allow: vec!["tool1".to_string(), "tool2".to_string()],
410 ask_before: vec!["tool3".to_string()],
411 never_allow: vec!["tool4".to_string()],
412 };
413
414 let permissions = migrate_permission_config(&config, "user", PermissionScope::Global);
415
416 assert_eq!(permissions.len(), 4);
417
418 let tool1 = permissions.iter().find(|p| p.tool == "tool1").unwrap();
420 assert!(tool1.allowed);
421 assert_eq!(tool1.priority, 100);
422
423 let tool2 = permissions.iter().find(|p| p.tool == "tool2").unwrap();
424 assert!(tool2.allowed);
425 assert_eq!(tool2.priority, 100);
426
427 let tool3 = permissions.iter().find(|p| p.tool == "tool3").unwrap();
429 assert!(tool3.allowed);
430 assert_eq!(tool3.priority, 50);
431 assert_eq!(
432 tool3.metadata.get("requires_confirmation"),
433 Some(&serde_json::Value::Bool(true))
434 );
435
436 let tool4 = permissions.iter().find(|p| p.tool == "tool4").unwrap();
438 assert!(!tool4.allowed);
439 assert_eq!(tool4.priority, 100);
440 }
441
442 #[test]
443 fn test_migrate_known_tools() {
444 let mut manager = create_test_permission_manager();
445 manager.update_user_permission("tool1", PermissionLevel::AlwaysAllow);
446 manager.update_user_permission("tool2", PermissionLevel::AskBefore);
447 manager.update_user_permission("tool3", PermissionLevel::NeverAllow);
448
449 let result = migrate_known_tools(
450 &manager,
451 &["tool1", "tool2", "tool3", "tool4"],
452 PermissionScope::Global,
453 );
454
455 assert_eq!(result.permissions.len(), 3);
456 assert_eq!(result.always_allow_count, 1);
457 assert_eq!(result.ask_before_count, 1);
458 assert_eq!(result.never_allow_count, 1);
459 assert_eq!(result.total_count(), 3);
460 }
461
462 #[test]
463 fn test_is_migrated_permission() {
464 let migrated = migrate_permission_level(
465 "test_tool",
466 PermissionLevel::AlwaysAllow,
467 PermissionScope::Global,
468 );
469 assert!(is_migrated_permission(&migrated));
470
471 let not_migrated = ToolPermission {
472 tool: "test_tool".to_string(),
473 allowed: true,
474 ..Default::default()
475 };
476 assert!(!is_migrated_permission(¬_migrated));
477 }
478
479 #[test]
480 fn test_get_original_permission_level() {
481 let always_allow = migrate_permission_level(
482 "tool1",
483 PermissionLevel::AlwaysAllow,
484 PermissionScope::Global,
485 );
486 assert_eq!(
487 get_original_permission_level(&always_allow),
488 Some(PermissionLevel::AlwaysAllow)
489 );
490
491 let ask_before =
492 migrate_permission_level("tool2", PermissionLevel::AskBefore, PermissionScope::Global);
493 assert_eq!(
494 get_original_permission_level(&ask_before),
495 Some(PermissionLevel::AskBefore)
496 );
497
498 let never_allow = migrate_permission_level(
499 "tool3",
500 PermissionLevel::NeverAllow,
501 PermissionScope::Global,
502 );
503 assert_eq!(
504 get_original_permission_level(&never_allow),
505 Some(PermissionLevel::NeverAllow)
506 );
507
508 let not_migrated = ToolPermission::default();
509 assert_eq!(get_original_permission_level(¬_migrated), None);
510 }
511
512 #[test]
513 fn test_migration_result_total_count() {
514 let mut result = MigrationResult::new();
515 result.always_allow_count = 5;
516 result.ask_before_count = 3;
517 result.never_allow_count = 2;
518
519 assert_eq!(result.total_count(), 10);
520 }
521
522 #[test]
523 fn test_migrate_empty_config() {
524 let config = PermissionConfig::default();
525 let permissions = migrate_permission_config(&config, "user", PermissionScope::Global);
526 assert!(permissions.is_empty());
527 }
528
529 #[test]
530 fn test_migrate_preserves_tool_names() {
531 let config = PermissionConfig {
532 always_allow: vec!["prefix__tool_name".to_string()],
533 ask_before: vec!["another__tool".to_string()],
534 never_allow: vec!["dangerous_tool".to_string()],
535 };
536
537 let permissions = migrate_permission_config(&config, "user", PermissionScope::Global);
538
539 assert!(permissions.iter().any(|p| p.tool == "prefix__tool_name"));
540 assert!(permissions.iter().any(|p| p.tool == "another__tool"));
541 assert!(permissions.iter().any(|p| p.tool == "dangerous_tool"));
542 }
543}