From 32d36d5eb9ebdca5c17713b47090388fba71e3bb Mon Sep 17 00:00:00 2001
From: Tanner Prestegard <tanner.prestegard@ligo.org>
Date: Wed, 22 Aug 2018 13:31:24 -0500
Subject: [PATCH] Global and object permission filter for viewsets

The built-in DjangoObjectPermissionsFilter from DRF doesn't
accept global (table-level) permissions as higher-level than
object permissions. This filter replaces the built-in one
and sets the 'accept_global_perms' argument to True so that
global permissions are accepted when filtering objects based on
user view permissions.
---
 gracedb/superevents/api/filters.py | 28 ++++++++++++++++++++++++++++
 gracedb/superevents/api/views.py   | 17 +++++------------
 2 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/gracedb/superevents/api/filters.py b/gracedb/superevents/api/filters.py
index 797955cf3..f07078d76 100644
--- a/gracedb/superevents/api/filters.py
+++ b/gracedb/superevents/api/filters.py
@@ -82,3 +82,31 @@ class SupereventOrderingFilter(filters.OrderingFilter):
 
         # No ordering was included, or all the ordering fields were invalid
         return self.get_default_ordering(view)
+
+
+class DjangoObjectAndGlobalPermissionsFilter(
+    filters.DjangoObjectPermissionsFilter):
+    """
+    Same as DjangoObjectPermissionsFilter, except it allows global permissions.
+    """
+    accept_global_perms = True
+
+    def filter_queryset(self, request, queryset, view):
+        # Mostly from rest_framework.filters.DjangoObjectPermissionsFilter
+        #
+        # We want to defer this import until run-time, rather than import-time.
+        # See https://github.com/encode/django-rest-framework/issues/4608
+        # (Also see #1624 for why we need to make this import explicitly)
+        from guardian.shortcuts import get_objects_for_user
+
+        extra = {}
+        user = request.user
+        model_cls = queryset.model
+        kwargs = {
+            'app_label': model_cls._meta.app_label,
+            'model_name': model_cls._meta.model_name
+        }
+        permission = self.perm_format % kwargs
+        extra['accept_global_perms'] = self.accept_global_perms
+
+        return get_objects_for_user(user, permission, queryset, **extra)
diff --git a/gracedb/superevents/api/views.py b/gracedb/superevents/api/views.py
index 5743be0e7..830d34d79 100644
--- a/gracedb/superevents/api/views.py
+++ b/gracedb/superevents/api/views.py
@@ -26,7 +26,8 @@ from events.view_utils import reverse as gracedb_reverse
 from events.api.backends import LigoAuthentication
 
 from ..buildVOEvent import VOEventBuilderException
-from .filters import SupereventSearchFilter, SupereventOrderingFilter
+from .filters import SupereventSearchFilter, SupereventOrderingFilter, \
+    DjangoObjectAndGlobalPermissionsFilter
 from .mixins import GetParentSupereventMixin, BaseGetObjectMixin, \
     SafeDestroyMixin, SafeCreateMixin
 from .paginators import BasePaginationFactory, CustomLabelPagination, \
@@ -52,7 +53,8 @@ class SupereventViewSet(SafeCreateMixin, viewsets.ModelViewSet):
     pagination_class = CustomSupereventPagination
     lookup_field = SUPEREVENT_LOOKUP_FIELD
     lookup_value_regex = SUPEREVENT_LOOKUP_REGEX
-    filter_backends = (SupereventSearchFilter, SupereventOrderingFilter,)
+    filter_backends = (DjangoObjectAndGlobalPermissionsFilter,
+        SupereventSearchFilter, SupereventOrderingFilter,)
     ordering_fields = ('created', 't_0', 't_start', 't_end',
         'preferred_event__id', 't_0_date', 'is_gw', 'base_date_number',
         'gw_date_number', 'category')
@@ -64,15 +66,6 @@ class SupereventViewSet(SafeCreateMixin, viewsets.ModelViewSet):
             serializer_class = SupereventUpdateSerializer
         return serializer_class
 
-    def get_queryset(self):
-        """Filter queryset for user"""
-        # TODO: do we need to filter this any further?
-        # TODO: Check that this might be causing slowness
-        #queryset = get_objects_for_user(self.request.user,
-        #    'superevents.view_superevent')
-        queryset = self.queryset
-        return queryset
-
     def get_object(self):
         queryset = self.filter_queryset(self.get_queryset())
         superevent_id = self.kwargs.get(self.lookup_field)
@@ -172,9 +165,9 @@ class SupereventLogViewSet(mixins.ListModelMixin,
     parser_class = parsers.FileUploadParser
     serializer_class = SupereventLogSerializer
     pagination_class = BasePaginationFactory(results_name='log')
+    filter_backends = (DjangoObjectAndGlobalPermissionsFilter,)
     lookup_field = 'N'
 
-    # TODO: filter logs for viewers
     def get_queryset(self):
         superevent = self.get_parent()
         queryset = superevent.log_set.all().order_by('N')
-- 
GitLab