fix(manager-core): F-S-005 gate dashboard delete_user on AppUsersAdmin
users_admin_api::delete_user was a comment-only acknowledgement that "this should require AppUsersAdmin" while actually calling service.delete which only gates on AppUsersWrite. v1.1.8 HANDBACK §7 listed this as known. Effect: any editor could delete app users via the admin HTTP API and dashboard button. Add an explicit `authz::require(... AppUsersAdmin(app_id))` before the service call. Delete the stale comment. AUDIT.md anchor: F-S-005. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::app_repo::AppRepository;
|
||||
use crate::authz::{AuthzDenied, AuthzRepo};
|
||||
use crate::authz::{self, AuthzDenied, AuthzRepo, Capability};
|
||||
use crate::repo::ScriptRepositoryError;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -253,15 +253,16 @@ async fn delete_user(
|
||||
Path((id_or_slug, user_id)): Path<(String, Uuid)>,
|
||||
) -> Result<StatusCode, AppUsersApiError> {
|
||||
let app = resolve_app(&*s.apps, &id_or_slug).await?;
|
||||
// Per brief: DELETE is gated on AppUsersAdmin (more senior than
|
||||
// the SDK's delete which is gated on AppUsersWrite — the dashboard
|
||||
// delete button is an irreversible administrative action). The
|
||||
// service's `delete` checks AppUsersWrite, so we apply the
|
||||
// stronger Admin check here and bypass the service. Actually we
|
||||
// can keep this clean by going via service.delete which gates on
|
||||
// AppUsersWrite — admins always satisfy Write — and adding the
|
||||
// explicit Admin gate as a precondition would be redundant since
|
||||
// anyone with Admin satisfies Write. Documented in HANDBACK §7.
|
||||
// F-S-005: gate on the stronger AppUsersAdmin per the original brief
|
||||
// — the dashboard delete is irreversible. The service-layer
|
||||
// `delete` only checks AppUsersWrite; without this explicit gate,
|
||||
// any editor could delete app users via the admin HTTP API.
|
||||
authz::require(
|
||||
s.authz.as_ref(),
|
||||
&principal,
|
||||
Capability::AppUsersAdmin(app.id),
|
||||
)
|
||||
.await?;
|
||||
let cx = synth_cx(app.id, &principal);
|
||||
let removed = s.users.delete(&cx, AppUserId::from(user_id)).await?;
|
||||
if removed {
|
||||
|
||||
Reference in New Issue
Block a user