use std::sync::Arc; use argon2::password_hash::{SaltString, rand_core::OsRng}; use database::generated::entities::user; use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, TransactionTrait}; use tracing::{debug, info, warn}; use crate::configs::ProgramSettings; use crate::helpers::constants::ADMIN_INIT_SECRET_KEY; use crate::services::{ auth::{ authentication::strategies::password::PasswordStrategy, user::{NewUser, UserService, UserServiceImpl}, }, settings::{SettingsService, SettingsStore}, }; pub async fn init_admin( config: &ProgramSettings, db: Arc, ) -> Result<(), Box> { // if admin user already exists, skip let admin_exists = user::Entity::find() .filter(user::Column::IsAdmin.eq(true)) .filter(user::Column::IsActive.eq(true)) .one(db.as_ref()) .await .map_err(|err| format!("Failed to query for existing admin user: {}", err))? .is_some(); if admin_exists { debug!("Admin user already exists. Skipping admin initialization."); return Ok(()); } // if config contains admin init settings, run admin init if let (Some(username), Some(password)) = ( &config.auth.default_admin_username, &config.auth.default_admin_password, ) { let r = _init_admin(username, password, db.clone()).await; if let Err(e) = r { warn!("Failed to initialize admin user: {}", e); info!("Defaulting to manual creation from dashboard."); } else { return Ok(()); } } // else generate a random secret to be used when initializing admin from dashboard let secret = generate_admin_init_secret(db.clone()).await?; info!( "Admin initialization secret generated. Use this secret to initialize the admin user from the dashboard: {}. This secret will only be shown once and is only valid until the admin user is created or the application is restarted.", secret ); Ok(()) } async fn generate_admin_init_secret( db: Arc, ) -> Result> { let secret = SaltString::generate(&mut OsRng).as_str().to_owned(); // Store the secret in a settings table let setting = SettingsService::new(db.clone()); setting .set_setting(ADMIN_INIT_SECRET_KEY, secret.clone()) .await .map_err(|err| format!("Failed to store admin init secret: {}", err))?; Ok(secret) } async fn _init_admin( username: &str, password: &str, db: Arc, ) -> Result<(), Box> { info!("Initializing admin user..."); // Check if an admin user already exists let admin_exists = user::Entity::find() .filter(user::Column::IsAdmin.eq(true)) .one(db.as_ref()) .await? .is_some(); if admin_exists { debug!("Admin user already exists. Skipping initialization."); return Ok(()); } info!("No admin user found. Creating default admin user..."); let user_service = UserServiceImpl::new(db.clone()); let password_strategy = PasswordStrategy::new(db.clone()); let user = NewUser { username: username.to_string(), is_admin: true, }; let mut tx = db.begin().await?; // create user let user = user_service.create_user(user, Some(&mut tx)).await?; // create temporary password password_strategy .create_identity(user.id, password, Some(&mut tx)) .await?; // tx.commit().await?; info!( "Default admin user created successfully, username: {}", username ); Ok(()) }