use rusqlite::Connection; use std::path::PathBuf; use argon2::{self, Algorithm, Argon2, Params, PasswordHasher, Version}; use argon2::password_hash::{SaltString, rand_core::OsRng}; fn db_path() -> PathBuf { let base = std::env::var("MOMENTRY_DATA_DIR").unwrap_or_else(|_| "../data/users".to_string()); std::fs::create_dir_all(&base).ok(); PathBuf::from(base).join("demo.sqlite") } fn hash_password(password: &str) -> String { let salt = SaltString::generate(&mut OsRng); let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, Params::default()); argon2.hash_password(password.as_bytes(), &salt).unwrap().to_string() } fn main() { let args: Vec = std::env::args().collect(); if args.len() < 2 { print_usage(); std::process::exit(1); } let command = &args[1]; let path = db_path(); let conn = Connection::open(&path).expect("Failed to open database"); match command.as_str() { "list" => { list_users(&conn); } "create" => { if args.len() < 4 { println!("Usage: user_tool create "); std::process::exit(1); } let username = &args[2]; let password = &args[3]; create_user(&conn, username, password); } "set-password" => { if args.len() < 4 { println!("Usage: user_tool set-password "); std::process::exit(1); } let username = &args[2]; let password = &args[3]; set_password(&conn, username, password); } "delete" => { if args.len() < 3 { println!("Usage: user_tool delete "); std::process::exit(1); } let username = &args[2]; delete_user(&conn, username); } "init-demo" => { init_demo_user(&conn); } _ => { print_usage(); } } } fn print_usage() { println!("Momentry Studio User Management Tool"); println!(); println!("Usage:"); println!(" user_tool list - List all users"); println!(" user_tool create - Create a new user"); println!(" user_tool set-password - Change user password"); println!(" user_tool delete - Delete a user"); println!(" user_tool init-demo - Initialize demo user"); } fn list_users(conn: &Connection) { let mut stmt = conn .prepare("SELECT id, username, display_name, role FROM app_users") .expect("Failed to prepare statement"); let users = stmt .query_map([], |row| { Ok(( row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, Option>(2)?, row.get::<_, String>(3)?, )) }) .expect("Failed to query users"); println!("Users:"); println!("{:<10} {:<15} {:<20} {:<10}", "ID", "Username", "Display Name", "Role"); println!("{}", "-".repeat(60)); for user in users { let (id, username, display_name, role) = user.expect("Failed to get user"); let display = display_name.unwrap_or_else(|| "-".to_string()); println!("{:<10} {:<15} {:<20} {:<10}", id, username, display, role); } } fn create_user(conn: &Connection, username: &str, password: &str) { let password_hash = hash_password(password); let id = format!("user_{}", chrono::Utc::now().timestamp()); conn.execute( "INSERT INTO app_users (id, username, password_hash, role) VALUES (?1, ?2, ?3, 'user')", rusqlite::params![id, username, password_hash], ) .expect("Failed to create user"); println!("✅ User '{}' created successfully", username); } fn set_password(conn: &Connection, username: &str, password: &str) { let password_hash = hash_password(password); let rows = conn .execute( "UPDATE app_users SET password_hash = ?1 WHERE username = ?2", rusqlite::params![password_hash, username], ) .expect("Failed to update password"); if rows > 0 { println!("✅ Password updated for user '{}'", username); } else { println!("❌ User '{}' not found", username); } } fn delete_user(conn: &Connection, username: &str) { let rows = conn .execute( "DELETE FROM app_users WHERE username = ?1", rusqlite::params![username], ) .expect("Failed to delete user"); if rows > 0 { println!("✅ User '{}' deleted", username); } else { println!("❌ User '{}' not found", username); } } fn init_demo_user(conn: &Connection) { let default_password = std::env::var("MOMENTRY_DEFAULT_PASSWORD") .unwrap_or_else(|_| { use std::time::{SystemTime, UNIX_EPOCH}; let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); format!("demo_{}", timestamp % 1000000) }); let password_hash = hash_password(&default_password); conn.execute( "INSERT OR REPLACE INTO app_users (id, username, password_hash, display_name, role) VALUES ('demo', 'demo', ?1, 'Demo User', 'admin')", rusqlite::params![password_hash], ) .expect("Failed to create demo user"); println!("========================================"); println!("Demo user initialized:"); println!(" Username: demo"); println!(" Password: {}", default_password); println!("========================================"); }