144 lines
4.1 KiB
Rust
144 lines
4.1 KiB
Rust
use std::sync::Arc;
|
|
|
|
use axum::{
|
|
Json,
|
|
extract::State,
|
|
http::StatusCode,
|
|
response::{IntoResponse, Response},
|
|
};
|
|
use database::generated::entities::user;
|
|
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, TransactionTrait};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::{Value, from_value};
|
|
use tracing::{debug, error, info, warn};
|
|
|
|
use crate::{
|
|
helpers::constants::ADMIN_INIT_SECRET_KEY,
|
|
routes::{AppState, api::openapi::tag::AUTH_TAG},
|
|
services::auth::user::NewUser,
|
|
};
|
|
|
|
/// Login request payload
|
|
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct AdminInitRequest {
|
|
username: String,
|
|
password: String,
|
|
// The secret key required to initialize the admin user
|
|
setup_secret: String,
|
|
}
|
|
|
|
/// Initializes the admin user
|
|
///
|
|
/// Initializes the admin user if no admin user exists and the correct setup secret is provided.
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/auth/init_admin",
|
|
request_body = AdminInitRequest,
|
|
responses(
|
|
(status = 200, description = "Admin user initialized successfully"),
|
|
(status = 400, description = "Invalid request payload"),
|
|
(status = 401, description = "Unauthorized: Admin user already exists or invalid setup secret"),
|
|
(status = 500, description = "Internal server error"),
|
|
),
|
|
tag = AUTH_TAG,
|
|
)]
|
|
pub async fn init_admin(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(payload): Json<Value>,
|
|
) -> Response {
|
|
if user::Entity::find()
|
|
.filter(user::Column::IsAdmin.eq(true))
|
|
.filter(user::Column::IsActive.eq(true))
|
|
.one(state.database_connection.as_ref())
|
|
.await
|
|
.map_err(|err| {
|
|
error!("Failed to query for existing admin user: {}", err);
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
})
|
|
.unwrap_or(None)
|
|
.is_some()
|
|
{
|
|
warn!("Admin user already exists. Skipping admin initialization.");
|
|
return (StatusCode::UNAUTHORIZED).into_response();
|
|
}
|
|
|
|
let init_request: AdminInitRequest = match from_value(payload) {
|
|
Ok(req) => req,
|
|
Err(e) => {
|
|
warn!("Invalid login request: {}", e);
|
|
return (StatusCode::BAD_REQUEST).into_response();
|
|
}
|
|
};
|
|
|
|
let admin_secret = match state
|
|
.service
|
|
.settings
|
|
.get_setting(ADMIN_INIT_SECRET_KEY)
|
|
.await
|
|
{
|
|
Ok(secret) => secret,
|
|
Err(e) => {
|
|
error!(
|
|
"Failed to retrieve admin initialization secret. Invalid internal state?: {}",
|
|
e
|
|
);
|
|
return (StatusCode::INTERNAL_SERVER_ERROR).into_response();
|
|
}
|
|
};
|
|
|
|
if init_request.setup_secret != admin_secret {
|
|
info!("{},{}", init_request.setup_secret, admin_secret);
|
|
warn!("Invalid admin initialization secret provided.");
|
|
return (StatusCode::UNAUTHORIZED).into_response();
|
|
}
|
|
|
|
let mut tx = match state.database_connection.begin().await {
|
|
Ok(tx) => tx,
|
|
Err(e) => {
|
|
error!("Failed to start transaction: {}", e);
|
|
return (StatusCode::INTERNAL_SERVER_ERROR).into_response();
|
|
}
|
|
};
|
|
|
|
let user = match state
|
|
.service
|
|
.user
|
|
.create_user(
|
|
NewUser {
|
|
username: init_request.username,
|
|
is_admin: true,
|
|
},
|
|
Some(&mut tx),
|
|
)
|
|
.await
|
|
{
|
|
Ok(user) => user,
|
|
Err(e) => {
|
|
error!("Failed to initialize admin user: {}", e);
|
|
return (StatusCode::INTERNAL_SERVER_ERROR).into_response();
|
|
}
|
|
};
|
|
|
|
debug!("Created admin user with ID: {}", user.id);
|
|
match state
|
|
.service
|
|
.auth_state
|
|
.strategy
|
|
.password
|
|
.create_identity(user.id, &init_request.password, Some(&mut tx))
|
|
.await
|
|
{
|
|
Ok(_) => {}
|
|
Err(e) => {
|
|
error!("Failed to create admin user identity: {}", e);
|
|
return (StatusCode::INTERNAL_SERVER_ERROR).into_response();
|
|
}
|
|
};
|
|
|
|
tx.commit().await.unwrap_or_else(|e| {
|
|
error!("Failed to commit transaction: {}", e);
|
|
});
|
|
|
|
(StatusCode::OK).into_response()
|
|
}
|