diff --git a/README.md b/README.md index 74ed897a4448e19f1d8a1e20a0732bf58c768289..aad7896facd55b01ef0203aab9ac4a2a43ae36c9 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,8 @@ In the following example, besides the urls `/members/` and `/members/<pk>/`, two ## Filter Backends +### LocalObjectFilterBackend: Excluding distant resources + To achieve federation, DjangoLDP includes links to objects from federated servers and stores these as local objects (see 1.0 - Models). In some situations, you will want to exclude these from the queryset of a custom view To provide for this need, there is defined in `djangoldp.filters` a FilterBackend which can be included in custom viewsets to restrict the queryset to only objects which were created locally: @@ -337,17 +339,17 @@ class MyModel(models.Model): Now when an instance of `MyModel` is saved, its `author_user` property will be set to the **profile** of the authenticated user. -## permissions - -Django-Guardian is used by default to support object-level permissions. Custom permissions can be added to your model using this attribute. See the [Django-Guardian documentation](https://django-guardian.readthedocs.io/en/stable/userguide/assign.html) for more information - -## permissions_classes +### permissions_classes This allows you to add permissions for anonymous, logged in user, author ... in the url: By default `LDPPermissions` is used. Specific permissin classes can be developed to fit special needs. -## anonymous_perms, user_perms, owner_perms +For developing custom permissions classes using `LDPPermissions`, please see the DjangoLDP [guide on permisssions](https://git.startinblox.com/djangoldp-packages/djangoldp/wikis/guides/custom-permissions) + +Django-Guardian is used by default to support object-level permissions. Custom permissions can be added to your model using this attribute. See the [Django-Guardian documentation](https://django-guardian.readthedocs.io/en/stable/userguide/assign.html) for more information + +### anonymous_perms, user_perms, owner_perms Those allow you to set permissions from your model's meta. diff --git a/djangoldp/filters.py b/djangoldp/filters.py index 59afd6b909bf8d20a6425fa20c29bc225b3ac6a2..b9c9bd0168d6c9d61ca909e09e8cc2925dbef68f 100644 --- a/djangoldp/filters.py +++ b/djangoldp/filters.py @@ -1,5 +1,22 @@ from rest_framework.filters import BaseFilterBackend -from djangoldp.models import Model +from rest_framework_guardian.filters import ObjectPermissionsFilter + + +class LDPPermissionsFilterBackend(ObjectPermissionsFilter): + """ + Default FilterBackend for LDPPermissions. If user does not have model-level permissions, filters by + Django-Guardian's get_objects_for_user + """ + def filter_queryset(self, request, queryset, view): + from djangoldp.permissions import LDPPermissions + + # compares the requirement for GET, with what the user has on the MODEL + if LDPPermissions.has_model_view_permission(request, view.model): + return queryset + if not request.user.is_anonymous: + return super().filter_queryset(request, queryset, view) + # user is anonymous without anonymous permissions + return view.model.objects.none() class LocalObjectFilterBackend(BaseFilterBackend): @@ -8,6 +25,8 @@ class LocalObjectFilterBackend(BaseFilterBackend): For querysets which should only include local objects """ def filter_queryset(self, request, queryset, view): + from djangoldp.models import Model + internal_ids = [x.pk for x in queryset if not Model.is_external(x)] return queryset.filter(pk__in=internal_ids) @@ -18,6 +37,8 @@ class LocalObjectOnContainerPathBackend(LocalObjectFilterBackend): is the model container path """ def filter_queryset(self, request, queryset, view): + from djangoldp.models import Model + if issubclass(view.model, Model) and request.path_info == view.model.get_container_path(): return super(LocalObjectOnContainerPathBackend, self).filter_queryset(request, queryset, view) return queryset diff --git a/djangoldp/models.py b/djangoldp/models.py index 2c5e45118a4130b8eaa889ec7c2c651d8fa8cb95..e4670091da1a77f635927d1e5a5a30c99f4a61a6 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -58,6 +58,32 @@ class Model(models.Model): def __init__(self, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) + @classmethod + def filter_backends(cls): + '''constructs a list of filter_backends configured on the permissions classes applied to this model''' + filtered_classes = [p for p in cls.get_permission_classes(cls, [LDPPermissions]) if + hasattr(p, 'filter_backends') and p.filter_backends is not None] + filter_backends = list() + for p in filtered_classes: + filter_backends = list(set(filter_backends).union(set(p.filter_backends))) + return filter_backends + + @classmethod + def get_queryset(cls, request, view, queryset=None, model=None): + ''' + when serializing as a child of another resource (my model has a many-to-one relationship with some parent), + get_queryset is used to obtain the resources which should be displayed. This allows us to exclude those objects + which I do not have permission to view in an automatically generated serializer + ''' + if queryset is None: + queryset = cls.objects.all() + # this is a hack - sorry! https://git.startinblox.com/djangoldp-packages/djangoldp/issues/301/ + if model is not None: + view.model = model + for backend in list(cls.filter_backends()): + queryset = backend().filter_queryset(request, queryset, view) + return queryset + @classmethod def get_view_set(cls): '''returns the view_set defined in the model Meta or the LDPViewSet class''' @@ -239,7 +265,7 @@ class Model(models.Model): return None @classonlymethod - def get_permission_classes(cls, related_model, default_permissions_classes): + def get_permission_classes(cls, related_model, default_permissions_classes) -> LDPPermissions: '''returns the permission_classes set in the models Meta class''' return cls.get_meta(related_model, 'permission_classes', default_permissions_classes) diff --git a/djangoldp/permissions.py b/djangoldp/permissions.py index 1c3046cf64dffdcac4982027f588d71d7397b3c7..6be469add913d1cebf85db07fd94377c73989e12 100644 --- a/djangoldp/permissions.py +++ b/djangoldp/permissions.py @@ -4,18 +4,17 @@ from django.contrib.auth.models import _user_get_all_permissions from django.core.exceptions import PermissionDenied from django.db.models.base import ModelBase from rest_framework.permissions import DjangoObjectPermissions +from djangoldp.filters import LDPPermissionsFilterBackend class LDPPermissions(DjangoObjectPermissions): - """ - Default permissions - Anon: None - Auth: None but inherit from Anon - Owner: None but inherit from Auth - """ + # *DEFAULT* permissions for anon, auth and owner statuses anonymous_perms = ['view'] authenticated_perms = ['inherit'] owner_perms = ['inherit'] + # filter backends associated with the permissions class. This will be used to filter queryset in the (auto-generated) + # view for a model, and in the serializing nested fields + filter_backends = [LDPPermissionsFilterBackend] perms_cache = { 'time': time.time() @@ -33,11 +32,46 @@ class LDPPermissions(DjangoObjectPermissions): if (time.time() - cls.perms_cache['time']) > 5: cls.invalidate_cache() + @classmethod + def is_owner(cls, user, model, obj): + return obj and hasattr(model._meta, 'owner_field') and ( + getattr(obj, getattr(model._meta, 'owner_field')) == user + or (hasattr(user, 'urlid') and getattr(obj, getattr(model._meta, 'owner_field')) == user.urlid) + or getattr(obj, getattr(model._meta, 'owner_field')) == user.id) + + def _get_cache_key(self, model_name, user, obj): + user_key = 'None' if user is None else user.id + obj_key = 'None' if obj is None else obj.id + return 'User{}{}{}'.format(user_key, model_name, obj_key) + + @classmethod + def get_model_level_perms(cls, model, user, obj=None): + '''Auxiliary function returns the model-level anon-auth-owner permissions for a given, model, user and object''' + anonymous_perms = getattr(model._meta, 'anonymous_perms', cls.anonymous_perms) + authenticated_perms = getattr(model._meta, 'authenticated_perms', cls.authenticated_perms) + owner_perms = getattr(model._meta, 'owner_perms', cls.owner_perms) + + # 'inherit' permissions means inherit the permissions from the next level 'down' + if 'inherit' in authenticated_perms: + authenticated_perms = authenticated_perms + list(set(anonymous_perms) - set(authenticated_perms)) + if 'inherit' in owner_perms: + owner_perms = owner_perms + list(set(authenticated_perms) - set(owner_perms)) + + # apply user permissions and return + perms = set() + if user.is_anonymous: + perms = perms.union(set(anonymous_perms)) + else: + if cls.is_owner(user, model, obj): + perms = perms.union(set(owner_perms)) + else: + perms = perms.union(set(authenticated_perms)) + return perms + def user_permissions(self, user, obj_or_model, obj=None): """ Filter user permissions for a model class """ - self.refresh_cache() # this may be a permission for the model class, or an instance if isinstance(obj_or_model, ModelBase): @@ -45,49 +79,19 @@ class LDPPermissions(DjangoObjectPermissions): else: obj = obj_or_model model = obj_or_model.__class__ + model_name = model._meta.model_name perms_cache_key = self.cache_key(model, obj, user) if self.with_cache and perms_cache_key in self.perms_cache: return self.perms_cache[perms_cache_key] - # Get Anonymous permissions from Model's Meta. If not found use default - anonymous_perms = getattr(model._meta, 'anonymous_perms', self.anonymous_perms) - - # Get Auth permissions from Model's Meta. If not found use default - authenticated_perms = getattr(model._meta, 'authenticated_perms', self.authenticated_perms) - # Extend Auth if inherit is given - if 'inherit' in authenticated_perms: - authenticated_perms = authenticated_perms + list(set(anonymous_perms) - set(authenticated_perms)) - - # Get Owner permissions from Model's Meta. If not found use default - owner_perms = getattr(model._meta, 'owner_perms', self.owner_perms) - # Extend Owner if inherit is given - if 'inherit' in owner_perms: - owner_perms = owner_perms + list(set(authenticated_perms) - set(owner_perms)) - # return permissions - using set to avoid duplicates - # apply Django-Guardian (object-level) permissions - perms = set() + perms = self.get_model_level_perms(model, user, obj) if obj is not None and not user.is_anonymous: # get permissions from all backends and then remove model name from the permissions - model_name = model._meta.model_name forbidden_string = "_" + model_name - perms = set([p.replace(forbidden_string, '') for p in _user_get_all_permissions(user, obj)]) - - # apply anon, owner and auth permissions - if user.is_anonymous: - perms = perms.union(set(anonymous_perms)) - - else: - if obj and hasattr(model._meta, 'owner_field') and ( - getattr(obj, getattr(model._meta, 'owner_field')) == user - or (hasattr(user, 'urlid') and getattr(obj, getattr(model._meta, 'owner_field')) == user.urlid) - or getattr(obj, getattr(model._meta, 'owner_field')) == user.id): - perms = perms.union(set(owner_perms)) - - else: - perms = perms.union(set(authenticated_perms)) + perms = perms.union(set([p.replace(forbidden_string, '') for p in _user_get_all_permissions(user, obj)])) self.perms_cache[perms_cache_key] = list(perms) @@ -115,7 +119,8 @@ class LDPPermissions(DjangoObjectPermissions): 'DELETE': ['%(app_label)s.delete_%(model_name)s'], } - def get_permissions(self, method, obj): + @classmethod + def get_permissions(cls, method, obj): """ Translate perms_map to request """ @@ -125,10 +130,10 @@ class LDPPermissions(DjangoObjectPermissions): } # Only allows methods that are on perms_map - if method not in self.perms_map: + if method not in cls.perms_map: raise PermissionDenied - return [perm % kwargs for perm in self.perms_map[method]] + return [perm % kwargs for perm in cls.perms_map[method]] def has_permission(self, request, view): """ @@ -148,7 +153,7 @@ class LDPPermissions(DjangoObjectPermissions): model = view.model # get permissions required - perms = self.get_permissions(request.method, model) + perms = LDPPermissions.get_permissions(request.method, model) user_perms = self.user_permissions(request.user, model, obj) # compare them with the permissions I have @@ -170,13 +175,27 @@ class LDPPermissions(DjangoObjectPermissions): User does not have permission: 403 """ # get permissions required - perms = self.get_permissions(request.method, obj) + perms = LDPPermissions.get_permissions(request.method, obj) model = obj user_perms = self.user_permissions(request.user, model, obj) + return LDPPermissions.compare_permissions(perms, user_perms) + + @classmethod + def has_model_view_permission(cls, request, model): + ''' + shortcut to compare the requested user's permissions on the model-level + :return: True or False + ''' + # compare required permissions with those I have (on the model) + perms = LDPPermissions.get_permissions('GET', model) + user_perms = LDPPermissions.get_model_level_perms(model, request.user) + return cls.compare_permissions(perms, user_perms) + + @classmethod + def compare_permissions(self, perms, user_perms): # compare them with the permissions I have for perm in perms: if not perm.split('.')[-1].split('_')[0] in user_perms: return False - return True diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 28894baf0aa12d83c36472d2b91f2bca97d61423..89af73473e35efb57326c4097afa9f0e08cfde2e 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -105,10 +105,7 @@ class LDListMixin: cache_vary = str(self.context['request'].user) - if isinstance(value, QuerySet): - value = list(value) - - if not isinstance(value, Iterable): + if not isinstance(value, Iterable) and not isinstance(value, QuerySet): if not self.id.startswith('http'): self.id = '{}{}{}'.format(settings.BASE_URL, Model.resource(parent_model), self.id) @@ -116,7 +113,6 @@ class LDListMixin: if self.with_cache and self.to_representation_cache.has(cache_key, cache_vary): return self.to_representation_cache.get(cache_key, cache_vary) - filtered_values = value container_permissions = Model.get_permissions(child_model, self.context, ['view', 'add']) else: @@ -133,20 +129,19 @@ class LDListMixin: if self.with_cache and self.to_representation_cache.has(cache_key, cache_vary): return self.to_representation_cache.get(cache_key, cache_vary) - # remove objects from the list which I don't have permission to view - filtered_values = list( - filter(lambda v: Model.get_permission_classes(v, [LDPPermissions])[0]().has_object_permission( - self.context['request'], self.context['view'], v), value)) + # filter the queryset automatically based on child model permissions classes (filter_backends) + if isinstance(value, QuerySet) and hasattr(child_model, 'get_queryset'): + value = child_model.get_queryset(self.context['request'], self.context['view'], queryset=value, + model=child_model) + container_permissions = Model.get_permissions(child_model, self.context, ['add']) container_permissions.extend( Model.get_permissions(parent_model, self.context, ['view'])) self.to_representation_cache.set(self.id, cache_vary, {'@id': self.id, - '@type': 'ldp:Container', - 'ldp:contains': super().to_representation( - filtered_values), - 'permissions': container_permissions - }) + '@type': 'ldp:Container', + 'ldp:contains': super().to_representation(value), + 'permissions': container_permissions}) return self.to_representation_cache.get(self.id, cache_vary) @@ -278,7 +273,6 @@ class JsonLdRelatedField(JsonLdField): class JsonLdIdentityField(JsonLdField): '''Represents an identity (url) field for a serializer''' - def __init__(self, view_name=None, **kwargs): kwargs['read_only'] = True kwargs['source'] = '*' diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py index e3b1f39d5120f1abc70f05cd0dc0867e1fa84aee..b576ceb2be26134498ef956ad721048b622d0893 100644 --- a/djangoldp/tests/models.py +++ b/djangoldp/tests/models.py @@ -9,6 +9,7 @@ from django.utils.datetime_safe import date from djangoldp.fields import LDPUrlField from djangoldp.models import Model from djangoldp.permissions import LDPPermissions +from djangoldp.tests.permissions import HalfRandomPermissions class User(AbstractUser, Model): @@ -129,6 +130,7 @@ class LDPDummy(Model): class PermissionlessDummy(Model): some = models.CharField(max_length=255, blank=True, null=True) slug = models.SlugField(blank=True, null=True, unique=True) + parent = models.ForeignKey(LDPDummy, on_delete=models.DO_NOTHING, related_name="anons", blank=True, null=True) class Meta(Model.Meta): anonymous_perms = [] diff --git a/djangoldp/tests/permissions.py b/djangoldp/tests/permissions.py new file mode 100644 index 0000000000000000000000000000000000000000..f8ec6d3cc4eff41cd52647cd47d422ea2ece227f --- /dev/null +++ b/djangoldp/tests/permissions.py @@ -0,0 +1,35 @@ +from django.db.models import QuerySet +from django.db.models.base import ModelBase + +from djangoldp.permissions import LDPPermissions + + +class HalfRandomPermissions(LDPPermissions): + + def prefilter_query_set(self, query_set: QuerySet, request, view, model) -> QuerySet: + if request.user.is_anonymous: + return query_set.filter(pk__in=[2, 4, 6, 8]) + else: + return super().prefilter_query_set(query_set, request, view, model) + + def user_permissions(self, user, obj_or_model, obj=None): + if isinstance(obj_or_model, ModelBase): + model = obj_or_model + else: + obj = obj_or_model + model = obj_or_model.__class__ + + # perms_cache_key = self.cache_key(model, obj, user) + # if self.with_cache and perms_cache_key in self.perms_cache: + # return self.perms_cache[perms_cache_key] + + # start with the permissions set on the object and model + perms = set(super().user_permissions(user, obj_or_model, obj)) + + if obj is not None and not isinstance(obj, ModelBase) and user.is_anonymous: + if obj.pk % 2 == 0: + return ['add', 'view'] + else: + return [] + else: + return ['view'] diff --git a/djangoldp/tests/settings_default.py b/djangoldp/tests/settings_default.py index 3686e77775e31fcf1c30567e72db2896ae95c187..2634324cb0ab0b1b711d5f985156b7d03695d69b 100644 --- a/djangoldp/tests/settings_default.py +++ b/djangoldp/tests/settings_default.py @@ -93,3 +93,4 @@ LDP_RDF_CONTEXT={ } SEND_BACKLINKS=False GUARDIAN_AUTO_PREFETCH = True +SERIALIZER_CACHE = False diff --git a/djangoldp/tests/tests_guardian.py b/djangoldp/tests/tests_guardian.py index 0b51fa351fe5d4ddbcd0253305169d108f3dc33a..79a770fd154a232739c92555e113d4c430dd6be9 100644 --- a/djangoldp/tests/tests_guardian.py +++ b/djangoldp/tests/tests_guardian.py @@ -1,9 +1,11 @@ import json +import uuid from django.contrib.auth import get_user_model +from djangoldp.serializers import LDListMixin, LDPSerializer from rest_framework.test import APIClient, APITestCase from guardian.shortcuts import assign_perm -from .models import PermissionlessDummy, Dummy +from .models import PermissionlessDummy, Dummy, LDPDummy from djangoldp.permissions import LDPPermissions @@ -11,19 +13,32 @@ class TestsGuardian(APITestCase): def setUp(self): self.client = APIClient(enforce_csrf_checks=True) + LDPPermissions.invalidate_cache() + LDListMixin.to_representation_cache.reset() + LDPSerializer.to_representation_cache.reset() def setUpLoggedInUser(self): self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion') self.client.force_authenticate(user=self.user) - - # optional setup for testing PermissionlessDummy model with parameterised perms - def setUpGuardianDummyWithPerms(self, perms=[]): - self.dummy = PermissionlessDummy.objects.create(some='test', slug='test') + LDPPermissions.invalidate_cache() + LDListMixin.to_representation_cache.reset() + LDPSerializer.to_representation_cache.reset() + + def _get_dummy_with_perms(self, perms=None, parent=None): + if perms is None: + perms = [] + dummy = PermissionlessDummy.objects.create(some='test', slug=uuid.uuid4(), parent=parent) model_name = PermissionlessDummy._meta.model_name for perm in perms: - assign_perm(perm + '_' + model_name, self.user, self.dummy) + assign_perm(perm + '_' + model_name, self.user, dummy) + + return dummy + + # optional setup for testing PermissionlessDummy model with parameterised perms + def setUpGuardianDummyWithPerms(self, perms=None, parent=None): + self.dummy = self._get_dummy_with_perms(perms, parent) # test that dummy with no permissions set returns no results def test_get_dummy_no_permissions(self): @@ -53,7 +68,7 @@ class TestsGuardian(APITestCase): def test_patch_dummy_permission_granted(self): self.setUpLoggedInUser() - self.setUpGuardianDummyWithPerms(['change']) + self.setUpGuardianDummyWithPerms(['view', 'change']) body = {'some': "some_new"} response = self.client.patch('/permissionless-dummys/{}/'.format(self.dummy.slug), data=json.dumps(body), content_type='application/ld+json') @@ -88,3 +103,36 @@ class TestsGuardian(APITestCase): permissions = LDPPermissions() result = permissions.user_permissions(self.user, dummy) self.assertEqual(result.count('view'), 1) + + # TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/297 + '''def test_list_dummy_exception(self): + self.setUpLoggedInUser() + # I have permission on a permissionless dummy, but not in general + dummy_a = self._get_dummy_with_perms() + dummy_b = self._get_dummy_with_perms(['view']) + response = self.client.get('/permissionless-dummys/') + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data['ldp:contains']), 1) + self.assertNotIn(response.data['ldp:contains'], dummy_a.urlid) + self.assertIn(response.data['ldp:contains'], dummy_b.urlid)''' + + def test_list_dummy_exception_nested_view(self): + self.setUpLoggedInUser() + parent = LDPDummy.objects.create(some="test") + # two dummies, one I have permission to view and one I don't + dummy_a = self._get_dummy_with_perms(parent=parent) + dummy_b = self._get_dummy_with_perms(['view'], parent) + response = self.client.get('/ldpdummys/{}/anons/'.format(parent.pk)) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data['ldp:contains']), 1) + + def test_list_dummy_exception_nested_serializer(self): + self.setUpLoggedInUser() + parent = LDPDummy.objects.create(some="test") + # two dummies, one I have permission to view and one I don't + dummy_a = self._get_dummy_with_perms(parent=parent) + dummy_b = self._get_dummy_with_perms(['view'], parent) + response = self.client.get('/ldpdummys/{}/'.format(parent.pk)) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data['anons']['ldp:contains']), 1) + diff --git a/djangoldp/tests/tests_user_permissions.py b/djangoldp/tests/tests_user_permissions.py index 683b81686acdd88302e32ea269b335ad0264d255..2f61261ceb5d2403b72c60066aaf8cc42a1a5dbb 100644 --- a/djangoldp/tests/tests_user_permissions.py +++ b/djangoldp/tests/tests_user_permissions.py @@ -1,9 +1,11 @@ from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission, Group from rest_framework.test import APIClient, APITestCase -from .models import JobOffer +from .models import JobOffer, LDPDummy, PermissionlessDummy import json + class TestUserPermissions(APITestCase): def setUp(self): @@ -12,10 +14,36 @@ class TestUserPermissions(APITestCase): self.client.force_authenticate(user=self.user) self.job = JobOffer.objects.create(title="job", slug="slug1") + def setUpGroup(self): + self.group = Group.objects.create(name='Test') + view_perm = Permission.objects.get(codename='view_permissionlessdummy') + self.group.permissions.add(view_perm) + self.group.save() + + # list - simple def test_get_for_authenticated_user(self): response = self.client.get('/job-offers/') self.assertEqual(response.status_code, 200) + # list - I do not have permission from the model, but I do have permission via a Group I am assigned + # TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/291 + '''def test_group_list_access(self): + self.setUpGroup() + + response = self.client.get('/permissionless-dummys/') + self.assertEqual(response.status_code, 403) + + self.user.groups.add(self.group) + self.user.save() + response = self.client.get('/permissionless-dummys/') + self.assertEqual(response.status_code, 200)''' + + # TODO: repeat of the above test on nested field + '''def test_group_list_access_nested(self): + self.setUpGroup() + parent = LDPDummy.objects.create() + dummy = PermissionlessDummy.objects.create(parent=parent)''' + def test_get_1_for_authenticated_user(self): response = self.client.get('/job-offers/{}/'.format(self.job.slug)) self.assertEqual(response.status_code, 200) diff --git a/djangoldp/views.py b/djangoldp/views.py index 5980dc2949a8a732a394312e66e0feec7a1b78f1..85d1915415d8cdad17ea11445bc9aaf438c680ef 100644 --- a/djangoldp/views.py +++ b/djangoldp/views.py @@ -393,9 +393,9 @@ class LDPViewSet(LDPViewSetGenerator): # attach filter backends based on permissions classes, to reduce the queryset based on these permissions # https://www.django-rest-framework.org/api-guide/filtering/#generic-filtering if self.permission_classes: - for p in self.permission_classes: - if hasattr(p, 'filter_class') and p.filter_class: - self.filter_backends.append(p.filter_class) + filtered_classes = [p for p in self.permission_classes if hasattr(p, 'filter_backends') and p.filter_backends is not None] + for p in filtered_classes: + self.filter_backends = list(set(self.filter_backends).union(set(p.filter_backends))) self.serializer_class = self.build_read_serializer() self.write_serializer_class = self.build_write_serializer() @@ -448,37 +448,6 @@ class LDPViewSet(LDPViewSetGenerator): ''' return True - # def list(self, request, *args, **kwargs): - # t1 = time.time() - # queryset = self.get_queryset() - # t2 = time.time() - # print('got queryset in ' + str(t2 - t1)) - # - # t1 = time.time() - # queryset = self.filter_queryset(queryset) - # t2 = time.time() - # print('filtered queryset in ' + str(t2 - t1)) - # - # t1 = time.time() - # page = self.paginate_queryset(queryset) - # t2 = time.time() - # print('paginated queryset in ' + str(t2-t1)) - # if page is not None: - # t1 = time.time() - # serializer = self.get_serializer(page, many=True) - # paginated_response = self.get_paginated_response(serializer.data) - # t2 = time.time() - # print('paginated response in ' + str(t2-t1)) - # - # return paginated_response - # - # t1 = time.time() - # serializer = self.get_serializer(queryset, many=True) - # response = Response(serializer.data) - # t2 = time.time() - # print('regular response in ' + str(t2-t1)) - # return response - def create(self, request, *args, **kwargs): serializer = self.get_write_serializer(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/setup.cfg b/setup.cfg index 45f79b19bb1ab25ed0bba64bc5397d6322f7bbc0..ae8396a892c9fc8417ae1fb923487d96174cd421 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ install_requires = pyld==1.0.5 django-guardian==2.3.0 django-brotli + djangorestframework-guardian [options.extras_require] dev =