mark_deleted: atomic last-admin guard; review minors

This commit is contained in:
2026-10-26 02:16:35 +01:00
parent 0b5d18c4ee
commit 9a03de8956
5 changed files with 212 additions and 15 deletions

View File

@@ -8,13 +8,13 @@ use axum::{
};
use domain::{errors::DomainError, value_objects::UserId};
use crate::{errors::ApiError, state::AppState};
async fn ensure_active(state: &AppState, user_id: &UserId) -> Result<(), DomainError> {
application::users::ensure_active::execute(&state.app_ctx.deps.users.ensure_active, user_id)
.await
}
use crate::{errors::ApiError, state::AppState};
pub struct AuthenticatedUser(pub UserId);
impl<S> FromRequestParts<S> for AuthenticatedUser
@@ -82,8 +82,16 @@ where
.await
.ok();
let user_id = match user_id {
Some(id) if ensure_active(&app_state, &id).await.is_ok() => Some(id),
_ => None,
Some(id) => match ensure_active(&app_state, &id).await {
Ok(()) => Some(id),
Err(e) => {
if e.is_transient() {
tracing::warn!("ensure_active failed, treating as anonymous: {e:?}");
}
None
}
},
None => None,
};
Ok(OptionalCookieUser(user_id))
}
@@ -138,6 +146,7 @@ where
)
.await
.map_err(ApiError)?
// `None` only if the account vanished between `ensure_active` and here.
.ok_or_else(|| ApiError(DomainError::NotFound("user not found".into())))?;
if is_admin {
Ok(AdminApiUser(user_id))
@@ -166,6 +175,7 @@ where
)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response())?
// `None` only if the account vanished between `ensure_active` and here.
.ok_or_else(|| StatusCode::UNAUTHORIZED.into_response())?;
if is_admin {
Ok(AdminUser(user_id))