use axum::extract::{Request, State}; use axum::middleware::Next; use axum::response::{IntoResponse, Response}; use models::admin::admin_user; use sea_orm::EntityTrait; use session::Session; use crate::errors::AdminApiError; use crate::errors::password_change_required_response; use crate::services::service::AdminService; use crate::share::roles::AdminActor; use crate::share::session::{admin_uid, clear_admin, pwd_version_matches}; pub async fn admin_guard( State(service): State, session: Session, mut request: Request, next: Next, ) -> Response { let Some(uid) = admin_uid(&session) else { return AdminApiError::unauthorized().into_response(); }; let row = match admin_user::Entity::find_by_id(uid) .one(service.db().read()) .await { Ok(row) => row, Err(error) => return AdminApiError::internal(error).into_response(), }; let Some(row) = row else { clear_admin(&session); return AdminApiError::unauthorized().into_response(); }; if !row.is_active || !pwd_version_matches(&session, row.passwd_version) { clear_admin(&session); return AdminApiError::unauthorized().into_response(); } if row.must_change_password && !password_change_path_allowed(request.uri().path()) { return password_change_required_response(); } request.extensions_mut().insert(AdminActor { id: row.id, username: row.username, display_name: row.display_name, role: row.role, must_change_password: row.must_change_password, }); next.run(request).await } fn password_change_path_allowed(path: &str) -> bool { let path = path.trim_end_matches('/'); path.ends_with("/admin/me") || path.ends_with("/admin/me/password") || path.ends_with("/admin/auth/logout") }