change_password.rs 171 lignes · 5593 octets
//! Script CLI pour changer le mot de passe d'un utilisateur
//!
//! Usage:
//!   cargo run --bin change_password -- `<username>` `<nouveau_password>`
//!
//! Exemples:
//!   cargo run --bin change_password -- john-doe "NewPassword123!"
//!   cargo run --bin change_password -- admin "SecureAdminPass2024"
//!
//! Options:
//!   --keep-must-change    Garde le flag must_change_password tel quel
//!   --force-change        Force l'utilisateur à changer le password au prochain login

use rustwarden_core::config::database;
use rustwarden_core::models::user;
use rustwarden_core::services::password_service::PasswordService;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use std::env;

#[tokio::main]
async fn main() {
    // Load environment variables
    dotenvy::dotenv().ok();

    // Initialize basic logging
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
        )
        .init();

    // Parse arguments
    let args: Vec<String> = env::args().collect();

    if args.len() < 3 {
        eprintln!(
            "❌ Usage: {} <username> <nouveau_password> [options]",
            args[0]
        );
        eprintln!();
        eprintln!("Options:");
        eprintln!("  --keep-must-change    Garde le flag must_change_password tel quel");
        eprintln!(
            "  --force-change        Force l'utilisateur à changer le password au prochain login"
        );
        eprintln!();
        eprintln!("Exemples:");
        eprintln!("  {} john-doe \"NewPassword123!\"", args[0]);
        eprintln!("  {} admin \"SecurePass2024\" --force-change", args[0]);
        std::process::exit(1);
    }

    let username = &args[1];
    let new_password = &args[2];

    // Parse options
    let mut keep_must_change = false;
    let mut force_change = false;

    for arg in &args[3..] {
        match arg.as_str() {
            "--keep-must-change" => keep_must_change = true,
            "--force-change" => force_change = true,
            _ => {
                eprintln!("⚠️  Option inconnue: {}", arg);
            }
        }
    }

    // Validate password length (minimum 8 characters)
    if new_password.len() < 8 {
        eprintln!("❌ Erreur: Le mot de passe doit contenir au moins 8 caractères");
        std::process::exit(1);
    }

    println!(
        "🔐 Changement de mot de passe pour l'utilisateur: {}",
        username
    );
    println!();

    // Connect to database
    println!("📊 Connexion à la base de données...");
    let db = match database::create_connection().await {
        Ok(conn) => {
            println!("✅ Connexion établie");
            conn
        }
        Err(e) => {
            eprintln!("❌ Erreur de connexion à la base de données: {}", e);
            std::process::exit(1);
        }
    };

    // Find user by username
    println!("🔍 Recherche de l'utilisateur...");
    let user = match user::Entity::find()
        .filter(user::Column::Username.eq(username))
        .one(&db)
        .await
    {
        Ok(Some(user)) => {
            println!("✅ Utilisateur trouvé: {} ({})", user.username, user.email);
            user
        }
        Ok(None) => {
            eprintln!("❌ Erreur: Utilisateur '{}' introuvable", username);
            std::process::exit(1);
        }
        Err(e) => {
            eprintln!("❌ Erreur lors de la recherche: {}", e);
            std::process::exit(1);
        }
    };

    // Hash the new password
    println!("🔒 Hachage du nouveau mot de passe...");
    let password_hash = match PasswordService::hash_password(new_password).await {
        Ok(hash) => {
            println!("✅ Mot de passe haché avec succès");
            hash
        }
        Err(e) => {
            eprintln!("❌ Erreur lors du hachage: {}", e);
            std::process::exit(1);
        }
    };

    // Determine must_change_password value
    let must_change_password = if keep_must_change {
        println!(
            "ℹ️  Conservation du flag must_change_password existant: {}",
            user.must_change_password
        );
        user.must_change_password
    } else if force_change {
        println!("⚠️  L'utilisateur devra changer son mot de passe au prochain login");
        true
    } else {
        println!("✅ L'utilisateur pourra se connecter normalement");
        false
    };

    // Update user password
    println!("💾 Mise à jour du mot de passe dans la base de données...");
    let mut user_active: user::ActiveModel = user.into();
    user_active.password_hash = Set(password_hash);
    user_active.must_change_password = Set(must_change_password);
    user_active.updated_at = Set(chrono::Utc::now());

    match user_active.update(&db).await {
        Ok(updated_user) => {
            println!("✅ Mot de passe mis à jour avec succès!");
            println!();
            println!("📋 Détails:");
            println!("   Utilisateur: {}", updated_user.username);
            println!("   Email: {}", updated_user.email);
            println!(
                "   Must change password: {}",
                updated_user.must_change_password
            );
            println!("   Mis à jour: {}", updated_user.updated_at);
            println!();
            println!("🎉 Opération terminée avec succès!");
        }
        Err(e) => {
            eprintln!("❌ Erreur lors de la mise à jour: {}", e);
            std::process::exit(1);
        }
    }
}