Found reviewing #427 (object-level change-page actions).
permitted_action_names (api/object_actions.py) has two paths:
get_change_actions(request, ctx, pk) — django-object-actions' permission-aware hook; applies each action's allowed_permissions internally. Authoritative + filtered. Standard usage hits this.
- Fallback — a bare
change_actions attribute with no hook: declared names are surfaced as-is, gated only by the run view's has_change_permission(request, obj). Per-action allowed_permissions are not applied here.
Impact is LOW: standard django-object-actions installs always provide get_change_actions (the mixin), so the fallback only triggers for a hand-rolled change_actions list. And has_change_permission still gates the object. But for parity with the changelist/bulk action endpoints (which filter via Django's _filter_actions_by_permissions), the fallback should also drop any action whose declared allowed_permissions the user fails.
Fix
In the change_actions fallback, filter each name by its callable's allowed_permissions against has_<perm>_permission(request) (mirror _filter_actions_by_permissions), before returning.
Acceptance
- A
change_actions-only admin (no get_change_actions) with an action declaring allowed_permissions=["delete"] does not offer/run it for a change-only user.
Defense-in-depth, LOW severity. Tier 3/4.
Found reviewing #427 (object-level change-page actions).
permitted_action_names(api/object_actions.py) has two paths:get_change_actions(request, ctx, pk)— django-object-actions' permission-aware hook; applies each action'sallowed_permissionsinternally. Authoritative + filtered. Standard usage hits this.change_actionsattribute with no hook: declared names are surfaced as-is, gated only by the run view'shas_change_permission(request, obj). Per-actionallowed_permissionsare not applied here.Impact is LOW: standard django-object-actions installs always provide
get_change_actions(the mixin), so the fallback only triggers for a hand-rolledchange_actionslist. Andhas_change_permissionstill gates the object. But for parity with the changelist/bulk action endpoints (which filter via Django's_filter_actions_by_permissions), the fallback should also drop any action whose declaredallowed_permissionsthe user fails.Fix
In the
change_actionsfallback, filter each name by its callable'sallowed_permissionsagainsthas_<perm>_permission(request)(mirror_filter_actions_by_permissions), before returning.Acceptance
change_actions-only admin (noget_change_actions) with an action declaringallowed_permissions=["delete"]does not offer/run it for a change-only user.Defense-in-depth, LOW severity. Tier 3/4.