117 lines
3.6 KiB
Rust
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(())
|
|
}
|