diff --git a/src/fobi/permissions/generic.py b/src/fobi/permissions/generic.py index 9069e276..8cefb98a 100644 --- a/src/fobi/permissions/generic.py +++ b/src/fobi/permissions/generic.py @@ -122,13 +122,13 @@ class BasePermission(metaclass=BasePermissionMetaclass): """ Return `True` if permission is granted, `False` otherwise. """ - return True + return False def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ - return True + return False class AllowAnyPermission(BasePermission): @@ -142,6 +142,12 @@ class AllowAnyPermission(BasePermission): def has_permission(self, request, view): return True + def has_object_permission(self, request, view, obj): + """ + Return `True` if permission is granted, `False` otherwise. + """ + return True + class DenyAnyPermission(BasePermission): """ @@ -154,6 +160,9 @@ class DenyAnyPermission(BasePermission): def has_permission(self, request, view): return False + def has_object_permission(self, request, view, obj): + return False + class IsAuthenticatedPermission(BasePermission): """ @@ -163,6 +172,9 @@ class IsAuthenticatedPermission(BasePermission): def has_permission(self, request, view): return bool(request.user and request.user.is_authenticated) + def has_object_permission(self, request, view, obj): + return self.has_permission(request, view) + class IsAdminUserPermission(BasePermission): """ @@ -172,6 +184,9 @@ class IsAdminUserPermission(BasePermission): def has_permission(self, request, view): return bool(request.user and request.user.is_staff) + def has_object_permission(self, request, view, obj): + return self.has_permission(request, view) + class IsSuperUserPermission(BasePermission): """ @@ -181,6 +196,9 @@ class IsSuperUserPermission(BasePermission): def has_permission(self, request, view): return bool(request.user and request.user.is_superuser) + def has_object_permission(self, request, view, obj): + return self.has_permission(request, view) + class IsAuthenticatedOrReadOnlyPermission(BasePermission): """ @@ -193,3 +211,6 @@ class IsAuthenticatedOrReadOnlyPermission(BasePermission): or request.user and request.user.is_authenticated ) + + def has_object_permission(self, request, view, obj): + return self.has_permission(request, view) diff --git a/src/fobi/views/class_based.py b/src/fobi/views/class_based.py index 350cc0d2..f48bbcca 100644 --- a/src/fobi/views/class_based.py +++ b/src/fobi/views/class_based.py @@ -132,6 +132,19 @@ class PermissionMixin(View): code=getattr(permission, 'code', None) ) + def check_object_permissions(self, request, obj): + """Check if the request should be permitted for a given object. + + Raises an appropriate exception if the request is not permitted. + """ + for permission in self.get_permissions(): + if not permission.has_object_permission(request, self, obj): + self.permission_denied( + request, + message=getattr(permission, 'message', None), + code=getattr(permission, 'code', None) + ) + class AbstractDeletePluginEntryView(PermissionMixin, DeleteView): """Abstract delete view for plugin entries.""" @@ -153,10 +166,11 @@ class AbstractDeletePluginEntryView(PermissionMixin, DeleteView): # implementation. The message in the latter is fully custom, while # in this case we're stuck to Django's own implementation. # Comment added on 2022-07-10. - return get_object_or_404( + obj = get_object_or_404( self._get_queryset(self.request), pk=self.kwargs.get(self.pk_url_kwarg), ) + self.check_object_permissions(self.request, obj) # Add support for browsers which only accept GET and POST for now. def get(self, request, *args, **kwargs): @@ -425,6 +439,12 @@ class EditFormEntryView(PermissionMixin, UpdateView): .prefetch_related('formelemententry_set') \ .filter(user__pk=request.user.pk) + def get_object(self, queryset=None): + """Get object.""" + obj = super(EditFormEntryView, self).get_object(queryset) + self.check_object_permissions(self.request, obj) + return obj + def get(self, request, *args, **kwargs): self.object = self.get_object(queryset=self._get_queryset(request)) """Handle GET requests: instantiate a blank version of the form.""" @@ -533,11 +553,13 @@ class DeleteFormEntryView(PermissionMixin, DeletionMixin): def get_object(self, queryset=None): """Get object.""" - return get_object_or_404( + obj = get_object_or_404( FormEntry._default_manager.all(), pk=self.kwargs.get("form_entry_id"), user__pk=self.request.user.pk ) + self.check_object_permissions(self.request, obj) + return obj # Add support for browsers which only accept GET and POST for now. def get(self, request, *args, **kwargs): @@ -819,6 +841,12 @@ class EditFormElementEntryView(PermissionMixin, UpdateView): .select_related('form_entry', 'form_entry__user') \ .filter(form_entry__user__pk=request.user.pk) + def get_object(self, queryset=None): + """Get object.""" + obj = super(EditFormElementEntryView, self).get_object(queryset) + self.check_object_permissions(self.request, obj) + return obj + def get_essential_objects( self, form_element_entry_id, @@ -1234,6 +1262,12 @@ class EditFormHandlerEntryView(PermissionMixin, UpdateView): .select_related('form_entry') \ .filter(form_entry__user__pk=request.user.pk) + def get_object(self, queryset=None): + """Get object.""" + obj = super(EditFormHandlerEntryView, self).get_object(queryset) + self.check_object_permissions(self.request, obj) + return obj + def get_essential_objects( self, form_handler_entry_id, @@ -1417,9 +1451,14 @@ class AbstractViewFormEntryView(PermissionMixin, DetailView): permission_classes = (ViewFormEntryPermission,) def get_object(self, queryset=None): + """Get object.""" if queryset is None: queryset = self._get_queryset(request=self.request) - return super(AbstractViewFormEntryView, self).get_object(queryset=queryset) + obj = super(AbstractViewFormEntryView, self).get_object( + queryset=queryset, + ) + self.check_object_permissions(self.request, obj) + return obj def _get_queryset(self, request): """Get queryset.""" @@ -1493,6 +1532,12 @@ class ViewFormEntryView(AbstractViewFormEntryView): return render(request, template_name, context) + def get_object(self, queryset=None): + """Get object.""" + obj = super(ViewFormEntryView, self).get_object(queryset=queryset) + self.check_object_permissions(self.request, obj) + return obj + def get(self, request, *args, **kwargs): """Handle GET requests: instantiate a blank version of the form.""" try: