diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6115a0ba6856dd1a9f9833ca2c56f410b0d0b560..ccd3c2216cff2bea23034b13d82d0a94ea74fd6d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,12 @@ Fixed * The menu button used to be displayed twice on smaller screens. +Changed +~~~~~~~ + +* [Dev] ActionForm now checks permissions on objects before executing +* [Dev] ActionForm now returns a proper return value from the executed action + 2.8.1`_ - 2022-03-13 -------------------- diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index ef4184812f73d22bb1dcfa1ff84f74f722c3c774..3fb938822b093e250299a3fac04f0e0358e6f47a 100644 --- a/aleksis/core/forms.py +++ b/aleksis/core/forms.py @@ -39,7 +39,7 @@ from .registries import ( site_preferences_registry, ) from .util.auth_helpers import AppScopes -from .util.core_helpers import get_site_preferences +from .util.core_helpers import get_site_preferences, queryset_rules_filter class PersonForm(ExtensibleForm): @@ -722,17 +722,37 @@ class ActionForm(forms.Form): self.fields["selected_objects"].queryset = self.queryset self.fields["action"].choices = self._get_action_choices() - def execute(self) -> bool: + def clean_action(self): + action = self._get_actions_dict().get(self.cleaned_data["action"], None) + if not action: + raise ValidationError(_("The selected action does not exist.")) + return action + + def clean_selected_objects(self): + action = self.cleaned_data["action"] + if hasattr(action, "permission"): + selected_objects = queryset_rules_filter( + self.request, self.cleaned_data["selected_objects"], action.permission + ) + if selected_objects.count() < self.cleaned_data["selected_objects"].count(): + raise ValidationError( + _("You do not have permission to run {} on all selected objects.").format( + getattr(value, "short_description", value.__name__) + ) + ) + return self.cleaned_data["selected_objects"] + + def execute(self) -> Any: """Execute the selected action on all selected objects. - :return: If the form is not valid, it will return ``False``. + :return: the return value of the action """ if self.is_valid(): data = self.cleaned_data["selected_objects"] - action = self._get_actions_dict()[self.cleaned_data["action"]] - action(None, self.request, data) - return True - return False + action = self.cleaned_data["action"] + return action(None, self.request, data) + + raise TypeError("execute() must be called on a pre-validated form.") class ListActionForm(ActionForm):