auth_framework/migration/
mod.rs

1//! Migration utilities for transitioning to role-system v1.0
2//!
3//! This module provides comprehensive migration tools to help users
4//! transition from legacy authorization systems to the unified
5//! role-system v1.0 approach with minimal disruption.
6
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, HashSet};
9use std::path::PathBuf;
10use thiserror::Error;
11use tokio::fs;
12
13pub mod analyzers;
14pub mod converters;
15pub mod executors;
16pub mod planners;
17pub mod validators;
18
19/// Migration-related errors
20#[derive(Error, Debug)]
21pub enum MigrationError {
22    #[error("Legacy system analysis failed: {0}")]
23    AnalysisError(String),
24
25    #[error("Migration plan generation failed: {0}")]
26    PlanningError(String),
27
28    #[error("Migration execution failed: {0}")]
29    ExecutionError(String),
30
31    #[error("Migration validation failed: {0}")]
32    ValidationError(String),
33
34    #[error("Rollback operation failed: {0}")]
35    RollbackError(String),
36
37    #[error("I/O error: {0}")]
38    IoError(#[from] std::io::Error),
39
40    #[error("Serialization error: {0}")]
41    SerializationError(#[from] serde_json::Error),
42}
43
44/// Type of legacy authorization system
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
46pub enum LegacySystemType {
47    /// Simple permission lists
48    PermissionBased,
49    /// Basic role-based access control
50    BasicRbac,
51    /// Attribute-based access control
52    Abac,
53    /// Custom authorization implementation
54    Custom(String),
55    /// Multiple mixed systems
56    Hybrid(Vec<LegacySystemType>),
57}
58
59/// Legacy role definition
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct LegacyRole {
62    pub id: String,
63    pub name: String,
64    pub description: Option<String>,
65    pub permissions: Vec<String>,
66    pub parent_roles: Vec<String>,
67    pub metadata: HashMap<String, String>,
68}
69
70/// Legacy user assignment
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct LegacyUserAssignment {
73    pub user_id: String,
74    pub role_id: Option<String>,
75    pub permissions: Vec<String>,
76    pub attributes: HashMap<String, String>,
77    pub expiration: Option<chrono::DateTime<chrono::Utc>>,
78}
79
80/// Legacy permission definition
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct LegacyPermission {
83    pub id: String,
84    pub action: String,
85    pub resource: String,
86    pub conditions: HashMap<String, String>,
87    pub metadata: HashMap<String, String>,
88}
89
90/// Analysis result of legacy authorization system
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct LegacySystemAnalysis {
93    /// Type of legacy system detected
94    pub system_type: LegacySystemType,
95
96    /// Total number of roles found
97    pub role_count: usize,
98
99    /// Total number of permissions found
100    pub permission_count: usize,
101
102    /// Total number of user assignments
103    pub user_assignment_count: usize,
104
105    /// Discovered roles
106    pub roles: Vec<LegacyRole>,
107
108    /// Discovered permissions
109    pub permissions: Vec<LegacyPermission>,
110
111    /// User assignments
112    pub user_assignments: Vec<LegacyUserAssignment>,
113
114    /// Role hierarchy complexity (depth levels)
115    pub hierarchy_depth: usize,
116
117    /// Duplicate roles/permissions detected
118    pub duplicates_found: bool,
119
120    /// Orphaned permissions (not assigned to any role)
121    pub orphaned_permissions: Vec<String>,
122
123    /// Circular dependencies in role hierarchy
124    pub circular_dependencies: Vec<Vec<String>>,
125
126    /// Custom attributes that need special handling
127    pub custom_attributes: HashSet<String>,
128
129    /// Estimated migration complexity (1-10 scale)
130    pub complexity_score: u8,
131
132    /// Recommended migration strategy
133    pub recommended_strategy: MigrationStrategy,
134}
135
136/// Migration strategy options
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
138pub enum MigrationStrategy {
139    /// Direct mapping with minimal changes
140    DirectMapping,
141    /// Gradual migration with coexistence period
142    GradualMigration,
143    /// Complete rebuild with role consolidation
144    Rebuild,
145    /// Custom strategy for complex scenarios
146    Custom(String),
147}
148
149/// Migration plan for transitioning to role-system v1.0
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct MigrationPlan {
152    /// Unique plan identifier
153    pub id: String,
154
155    /// Source system analysis
156    pub source_analysis: LegacySystemAnalysis,
157
158    /// Selected migration strategy
159    pub strategy: MigrationStrategy,
160
161    /// Planned migration phases
162    pub phases: Vec<MigrationPhase>,
163
164    /// Role mapping from legacy to new system
165    pub role_mappings: HashMap<String, String>,
166
167    /// Permission mapping from legacy to new system
168    pub permission_mappings: HashMap<String, String>,
169
170    /// User assignment migrations
171    pub user_migrations: Vec<UserMigration>,
172
173    /// Pre-migration validation steps
174    pub pre_validation_steps: Vec<ValidationStep>,
175
176    /// Post-migration validation steps
177    pub post_validation_steps: Vec<ValidationStep>,
178
179    /// Rollback plan
180    pub rollback_plan: RollbackPlan,
181
182    /// Estimated migration time
183    pub estimated_duration: chrono::Duration,
184
185    /// Risk assessment
186    pub risk_level: RiskLevel,
187
188    /// Required downtime (if any)
189    pub downtime_required: Option<chrono::Duration>,
190}
191
192/// Individual migration phase
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct MigrationPhase {
195    pub id: String,
196    pub name: String,
197    pub description: String,
198    pub order: u32,
199    pub operations: Vec<MigrationOperation>,
200    pub dependencies: Vec<String>,
201    pub estimated_duration: chrono::Duration,
202    pub rollback_operations: Vec<MigrationOperation>,
203}
204
205/// Migration operation types
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub enum MigrationOperation {
208    CreateRole {
209        role_id: String,
210        name: String,
211        description: Option<String>,
212        permissions: Vec<String>,
213        parent_role: Option<String>,
214    },
215    AssignUserRole {
216        user_id: String,
217        role_id: String,
218        expiration: Option<chrono::DateTime<chrono::Utc>>,
219    },
220    CreatePermission {
221        permission_id: String,
222        action: String,
223        resource: String,
224        conditions: HashMap<String, String>,
225    },
226    MigrateCustomAttribute {
227        attribute_name: String,
228        conversion_logic: String,
229    },
230    ValidateIntegrity {
231        validation_type: String,
232        parameters: HashMap<String, String>,
233    },
234    Backup {
235        backup_location: PathBuf,
236        backup_type: BackupType,
237    },
238}
239
240/// User migration details
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct UserMigration {
243    pub user_id: String,
244    pub legacy_roles: Vec<String>,
245    pub legacy_permissions: Vec<String>,
246    pub new_roles: Vec<String>,
247    pub migration_notes: Option<String>,
248}
249
250/// Validation step definition
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct ValidationStep {
253    pub id: String,
254    pub name: String,
255    pub description: String,
256    pub validation_type: ValidationType,
257    pub parameters: HashMap<String, String>,
258    pub required: bool,
259}
260
261/// Types of validation
262#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
263pub enum ValidationType {
264    /// Check role hierarchy integrity
265    HierarchyIntegrity,
266    /// Validate permission consistency
267    PermissionConsistency,
268    /// Check user assignment validity
269    UserAssignmentValidity,
270    /// Verify no privilege escalation
271    PrivilegeEscalationCheck,
272    /// Custom validation script
273    Custom(String),
274}
275
276/// Rollback plan for migration
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct RollbackPlan {
279    pub phases: Vec<RollbackPhase>,
280    pub backup_locations: Vec<PathBuf>,
281    pub recovery_time_objective: chrono::Duration,
282    pub manual_steps: Vec<String>,
283}
284
285/// Rollback phase
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct RollbackPhase {
288    pub id: String,
289    pub name: String,
290    pub operations: Vec<MigrationOperation>,
291    pub order: u32,
292}
293
294/// Risk assessment levels
295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
296pub enum RiskLevel {
297    Low,
298    Medium,
299    High,
300    Critical,
301}
302
303/// Backup types
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub enum BackupType {
306    /// Full system backup
307    Full,
308    /// Incremental backup
309    Incremental,
310    /// Configuration only
311    ConfigOnly,
312    /// Data only
313    DataOnly,
314}
315
316/// Migration execution result
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct MigrationResult {
319    pub plan_id: String,
320    pub status: MigrationStatus,
321    pub started_at: chrono::DateTime<chrono::Utc>,
322    pub completed_at: Option<chrono::DateTime<chrono::Utc>>,
323    pub phases_completed: Vec<String>,
324    pub current_phase: Option<String>,
325    pub errors: Vec<String>,
326    pub warnings: Vec<String>,
327    pub metrics: MigrationMetrics,
328}
329
330/// Migration execution status
331#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
332pub enum MigrationStatus {
333    Planned,
334    InProgress,
335    Completed,
336    Failed,
337    RolledBack,
338    Paused,
339}
340
341/// Migration execution metrics
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct MigrationMetrics {
344    pub roles_migrated: usize,
345    pub permissions_migrated: usize,
346    pub users_migrated: usize,
347    pub errors_encountered: usize,
348    pub warnings_generated: usize,
349    pub validation_failures: usize,
350    pub rollback_count: usize,
351}
352
353/// Main migration manager
354pub struct MigrationManager {
355    /// Configuration for migration operations
356    config: MigrationConfig,
357}
358
359/// Migration manager configuration
360#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct MigrationConfig {
362    /// Working directory for migration files
363    pub working_directory: PathBuf,
364
365    /// Backup directory
366    pub backup_directory: PathBuf,
367
368    /// Maximum concurrent operations
369    pub max_concurrent_operations: usize,
370
371    /// Operation timeout
372    pub operation_timeout: chrono::Duration,
373
374    /// Enable dry-run mode
375    pub dry_run: bool,
376
377    /// Verbose logging
378    pub verbose: bool,
379}
380
381impl Default for MigrationConfig {
382    fn default() -> Self {
383        Self {
384            working_directory: PathBuf::from("./migration"),
385            backup_directory: PathBuf::from("./migration/backups"),
386            max_concurrent_operations: 4,
387            operation_timeout: chrono::Duration::minutes(30),
388            dry_run: false,
389            verbose: false,
390        }
391    }
392}
393
394impl MigrationManager {
395    /// Create new migration manager
396    pub fn new(config: MigrationConfig) -> Result<Self, MigrationError> {
397        // Ensure directories exist
398        std::fs::create_dir_all(&config.working_directory)?;
399        std::fs::create_dir_all(&config.backup_directory)?;
400
401        Ok(Self { config })
402    }
403
404    /// Analyze legacy authorization system
405    pub async fn analyze_legacy_system<P: AsRef<std::path::Path>>(
406        &self,
407        config_path: P,
408    ) -> Result<LegacySystemAnalysis, MigrationError> {
409        analyzers::analyze_legacy_system(config_path, &self.config).await
410    }
411
412    /// Generate migration plan
413    pub async fn generate_migration_plan(
414        &self,
415        analysis: &LegacySystemAnalysis,
416        strategy: Option<MigrationStrategy>,
417    ) -> Result<MigrationPlan, MigrationError> {
418        planners::generate_migration_plan(analysis, strategy, &self.config).await
419    }
420
421    /// Validate migration plan
422    pub async fn validate_migration_plan(
423        &self,
424        plan: &MigrationPlan,
425    ) -> Result<Vec<String>, MigrationError> {
426        validators::validate_migration_plan(plan, &self.config).await
427    }
428
429    /// Execute migration plan
430    pub async fn execute_migration(
431        &self,
432        plan: &MigrationPlan,
433    ) -> Result<MigrationResult, MigrationError> {
434        executors::execute_migration_plan(plan, &self.config).await
435    }
436
437    /// Get migration status
438    pub async fn get_migration_status(
439        &self,
440        plan_id: &str,
441    ) -> Result<Option<MigrationResult>, MigrationError> {
442        let status_file = self
443            .config
444            .working_directory
445            .join(format!("{}_status.json", plan_id));
446
447        if !status_file.exists() {
448            return Ok(None);
449        }
450
451        let content = fs::read_to_string(status_file).await?;
452        let result: MigrationResult = serde_json::from_str(&content)?;
453        Ok(Some(result))
454    }
455
456    /// Rollback migration
457    pub async fn rollback_migration(
458        &self,
459        plan: &MigrationPlan,
460    ) -> Result<MigrationResult, MigrationError> {
461        executors::rollback_migration(plan, &self.config).await
462    }
463
464    /// List available migration plans
465    pub async fn list_migration_plans(&self) -> Result<Vec<String>, MigrationError> {
466        let mut plans = Vec::new();
467        let mut entries = fs::read_dir(&self.config.working_directory).await?;
468
469        while let Some(entry) = entries.next_entry().await? {
470            let path = entry.path();
471            if path.extension().is_some_and(|ext| ext == "json")
472                && let Some(file_name) = path.file_stem()
473                && let Some(name) = file_name.to_str()
474                && name.ends_with("_plan")
475            {
476                plans.push(name.trim_end_matches("_plan").to_string());
477            }
478        }
479
480        Ok(plans)
481    }
482
483    /// Save migration plan to disk
484    pub async fn save_migration_plan(
485        &self,
486        plan: &MigrationPlan,
487    ) -> Result<PathBuf, MigrationError> {
488        let plan_file = self
489            .config
490            .working_directory
491            .join(format!("{}_plan.json", plan.id));
492        let content = serde_json::to_string_pretty(plan)?;
493        fs::write(&plan_file, content).await?;
494        Ok(plan_file)
495    }
496
497    /// Load migration plan from disk
498    pub async fn load_migration_plan(
499        &self,
500        plan_id: &str,
501    ) -> Result<MigrationPlan, MigrationError> {
502        let plan_file = self
503            .config
504            .working_directory
505            .join(format!("{}_plan.json", plan_id));
506        let content = fs::read_to_string(plan_file).await?;
507        let plan: MigrationPlan = serde_json::from_str(&content)?;
508        Ok(plan)
509    }
510
511    /// Generate migration report
512    pub async fn generate_migration_report(
513        &self,
514        result: &MigrationResult,
515    ) -> Result<String, MigrationError> {
516        let mut report = String::new();
517
518        report.push_str("# Migration Report\n\n");
519        report.push_str(&format!("**Plan ID**: {}\n", result.plan_id));
520        report.push_str(&format!("**Status**: {:?}\n", result.status));
521        report.push_str(&format!("**Started**: {}\n", result.started_at));
522
523        if let Some(completed) = result.completed_at {
524            report.push_str(&format!("**Completed**: {}\n", completed));
525            let duration = completed - result.started_at;
526            report.push_str(&format!(
527                "**Duration**: {} minutes\n",
528                duration.num_minutes()
529            ));
530        }
531
532        report.push_str("\n## Metrics\n\n");
533        report.push_str(&format!(
534            "- Roles migrated: {}\n",
535            result.metrics.roles_migrated
536        ));
537        report.push_str(&format!(
538            "- Permissions migrated: {}\n",
539            result.metrics.permissions_migrated
540        ));
541        report.push_str(&format!(
542            "- Users migrated: {}\n",
543            result.metrics.users_migrated
544        ));
545        report.push_str(&format!(
546            "- Errors: {}\n",
547            result.metrics.errors_encountered
548        ));
549        report.push_str(&format!(
550            "- Warnings: {}\n",
551            result.metrics.warnings_generated
552        ));
553
554        if !result.errors.is_empty() {
555            report.push_str("\n## Errors\n\n");
556            for error in &result.errors {
557                report.push_str(&format!("- {}\n", error));
558            }
559        }
560
561        if !result.warnings.is_empty() {
562            report.push_str("\n## Warnings\n\n");
563            for warning in &result.warnings {
564                report.push_str(&format!("- {}\n", warning));
565            }
566        }
567
568        Ok(report)
569    }
570}
571
572#[cfg(test)]
573mod tests {
574    use super::*;
575
576    #[tokio::test]
577    async fn test_migration_manager_creation() {
578        let config = MigrationConfig::default();
579        let manager = MigrationManager::new(config);
580        assert!(manager.is_ok());
581    }
582
583    #[test]
584    fn test_legacy_system_type_serialization() {
585        let system_type = LegacySystemType::BasicRbac;
586        let serialized = serde_json::to_string(&system_type).unwrap();
587        let deserialized: LegacySystemType = serde_json::from_str(&serialized).unwrap();
588        assert_eq!(system_type, deserialized);
589    }
590
591    #[test]
592    fn test_migration_strategy_serialization() {
593        let strategy = MigrationStrategy::GradualMigration;
594        let serialized = serde_json::to_string(&strategy).unwrap();
595        let deserialized: MigrationStrategy = serde_json::from_str(&serialized).unwrap();
596        assert_eq!(strategy, deserialized);
597    }
598
599    #[test]
600    fn test_risk_level_ordering() {
601        assert!(RiskLevel::Low < RiskLevel::Medium);
602        assert!(RiskLevel::Medium < RiskLevel::High);
603        assert!(RiskLevel::High < RiskLevel::Critical);
604    }
605}
606
607