Files
YANPM/apps/api/src/tasks/startup/init_admin.rs

117 lines
3.6 KiB
Rust

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<DatabaseConnection>,
) -> Result<(), Box<dyn std::error::Error>> {
// 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<DatabaseConnection>,
) -> Result<String, Box<dyn std::error::Error>> {
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<DatabaseConnection>,
) -> Result<(), Box<dyn std::error::Error>> {
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(())
}