Enforce admin-only for bulk and purge actions
Move admin checks from individual bulk actions to the main handler for `bulk.rs` and `case_actions.rs`. This consolidates the authorization logic for these sensitive operations. Additionally, refine the HTML template to conditionally render bulk action UI elements and the purge form only when the user is an admin. This ensures that non-admin users do not see or have access to these administrative functions. Update tests to reflect these changes, ensuring that non-admin users are correctly rejected for these actions and that the UI is properly hidden.
This commit is contained in:
@@ -37,6 +37,14 @@ pub async fn handle_bulk_action(
|
||||
State(events_tx): State<EventSender>,
|
||||
Form(form): Form<BulkForm>,
|
||||
) -> Result<Redirect, AppError> {
|
||||
// Bulk actions are admin-only across the board: the UI hides the
|
||||
// selection checkboxes and action bar from non-admins, and this
|
||||
// server-side gate defends against hand-crafted POSTs.
|
||||
if !user.is_admin() {
|
||||
warn!(slug = %user.slug, action = %form.action, "bulk: not admin");
|
||||
return Err(AppError::Forbidden("Nur für Admins".into()));
|
||||
}
|
||||
|
||||
let user_root = config.data_path.join(&user.slug);
|
||||
|
||||
match form.action.as_str() {
|
||||
@@ -52,13 +60,7 @@ pub async fn handle_bulk_action(
|
||||
.await
|
||||
}
|
||||
"close" => bulk_close(&events_tx, &user.slug, &user_root, &form.case_ids).await,
|
||||
"reset" => {
|
||||
if !user.is_admin() {
|
||||
warn!(slug = %user.slug, "bulk-reset: not admin");
|
||||
return Err(AppError::Forbidden("Nur für Admins".into()));
|
||||
}
|
||||
bulk_reset(&events_tx, &user.slug, &user_root, &form.case_ids).await
|
||||
}
|
||||
"reset" => bulk_reset(&events_tx, &user.slug, &user_root, &form.case_ids).await,
|
||||
other => {
|
||||
warn!(slug = %user.slug, action = %other, "bulk: unknown action");
|
||||
return Err(AppError::BadRequest("Unbekannte Aktion".into()));
|
||||
|
||||
@@ -460,6 +460,12 @@ pub async fn handle_purge_closed(
|
||||
State(events_tx): State<EventSender>,
|
||||
Form(form): Form<PurgeClosedForm>,
|
||||
) -> Result<Redirect, AppError> {
|
||||
// Destructive, irreversible — admin-only. UI hides the button for
|
||||
// non-admins; this is the server-side defense-in-depth gate.
|
||||
if !user.is_admin() {
|
||||
warn!(slug = %user.slug, "purge-closed: not admin");
|
||||
return Err(AppError::Forbidden("Nur für Admins".into()));
|
||||
}
|
||||
if form.confirm != "yes" {
|
||||
warn!(slug = %user.slug, "purge-closed: missing confirm=yes");
|
||||
return Err(AppError::BadRequest("Missing confirm=yes".into()));
|
||||
|
||||
Reference in New Issue
Block a user