diff --git a/djangoldp/models.py b/djangoldp/models.py index 5443bd09bd1e4056cfbc009a2030342663e2548d..6c5ac7c417bae1b148db483d5cf986cff18b87fa 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -74,6 +74,12 @@ class Model(models.Model): view, args, kwargs = get_resolver().resolve(id) return view.initkwargs['model'].objects.get(**kwargs) + @classonlymethod + def resolve_parent(cls, path): + split = path.strip('/').split('/') + parent_path = "/".join(split[0:len(split) - 1]) + return Model.resolve_id(parent_path) + @classonlymethod def resolve_container(cls, path): path = cls.__clean_path(path) diff --git a/djangoldp/permissions.py b/djangoldp/permissions.py index e22e39b754ff09912baced5ac27f12e7e26d82f4..f83c6e102e5a9ccf657b4d77f3f7782d5f572007 100644 --- a/djangoldp/permissions.py +++ b/djangoldp/permissions.py @@ -1,6 +1,7 @@ -from rest_framework.permissions import BasePermission from django.core.exceptions import PermissionDenied from django.db.models.base import ModelBase +from django.urls import Resolver404 +from rest_framework.permissions import BasePermission class LDPPermissions(BasePermission): @@ -14,10 +15,18 @@ class LDPPermissions(BasePermission): authenticated_perms = ['inherit'] owner_perms = ['inherit'] - def user_permissions(self, user, model, obj=None): + def user_permissions(self, user, obj_or_model, obj=None): """ Filter user permissions for a model class """ + + # sorted out param mess + if isinstance(obj_or_model, ModelBase): + model = obj_or_model + else: + obj = obj_or_model + model = obj_or_model.__class__ + # Get Anonymous permissions from Model's Meta. If not found use default anonymous_perms = getattr(model._meta, 'anonymous_perms', self.anonymous_perms) @@ -37,7 +46,9 @@ class LDPPermissions(BasePermission): return anonymous_perms else: - if obj and hasattr(model._meta, 'owner_field') and (getattr(obj, getattr(model._meta, 'owner_field')) == user or getattr(obj, getattr(model._meta, 'owner_field')) == user.id): + if obj and hasattr(model._meta, 'owner_field') and ( + getattr(obj, getattr(model._meta, 'owner_field')) == user or getattr(obj, getattr(model._meta, + 'owner_field')) == user.id): return owner_perms else: @@ -45,14 +56,7 @@ class LDPPermissions(BasePermission): def filter_user_perms(self, user, obj_or_model, permissions): # Only used on Model.get_permissions to translate permissions to LDP - if isinstance(obj_or_model, ModelBase): - model = obj_or_model - obj = None - else: - obj = obj_or_model - model = obj_or_model.__class__ - return [perm for perm in permissions if perm in self.user_permissions(user, model, obj)] - + return [perm for perm in permissions if perm in self.user_permissions(user, obj_or_model)] perms_map = { 'GET': ['%(app_label)s.view_%(model_name)s'], @@ -83,14 +87,20 @@ class LDPPermissions(BasePermission): """ Access to containers """ - - model = view.model - perms = self.get_permissions(request.method, model) + from djangoldp.models import Model + + if self.is_a_container(request._request.path): + try: + obj = Model.resolve_parent(request.path) + model = view.parent_model + except Resolver404: + obj = None + model = view.model + else: + obj = Model.resolve_id(request._request.path) + model = view.model - try: - obj = view.model.resolve_id(request._request.path) - except: - obj = None + perms = self.get_permissions(request.method, model) for perm in perms: if not perm.split('.')[1].split('_')[0] in self.user_permissions(request.user, model, obj): @@ -98,6 +108,11 @@ class LDPPermissions(BasePermission): return True + def is_a_container(self, path): + from djangoldp.models import Model + container, id = Model.resolve(path) + return id is None + def has_object_permission(self, request, view, obj): """ Access to objects @@ -111,4 +126,4 @@ class LDPPermissions(BasePermission): if not perm.split('.')[1].split('_')[0] in self.user_permissions(request.user, model, obj): return False - return True \ No newline at end of file + return True diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 15a4c897e1f1322f787285bc04cda47760f380fb..0df95085ca62d9e73b61a0f9f8517b21ef721e16 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -1,4 +1,4 @@ -from collections import OrderedDict, Mapping +from collections import OrderedDict, Mapping, Iterable from typing import Any from urllib import parse @@ -40,14 +40,39 @@ class LDListMixin: return [getattr(self, self.child_attr).to_internal_value(item) for item in data] def to_representation(self, value): + ''' + Permission on container : + - Can Add if add permission on contained object's type + - Can view the container is view permission on container model : container obj are filtered by view permission + ''' try: - model = getattr(self, self.child_attr).Meta.model + child_model = getattr(self, self.child_attr).Meta.model except AttributeError: - model = value.model + child_model = value.model + parent_model = None + if isinstance(value, QuerySet): + value = list(value) + + if not isinstance(value, Iterable): + filtered_values = value + container_permissions = Model.get_permissions(child_model, self.context['request'].user, ['view', 'add']) + else: + try: + parent_model = Model.resolve_parent(self.context['request'].path) + except: + parent_model = child_model + + filtered_values = list( + filter(lambda v: Model.get_permission_classes(v, [LDPPermissions])[0]().has_object_permission( + self.context['request'], self.context['view'], v), value)) + container_permissions = Model.get_permissions(child_model, self.context['request'].user, ['add']) + container_permissions.extend( + Model.get_permissions(parent_model, self.context['request'].user, + ['view'])) return {'@id': self.id, '@type': 'ldp:Container', - 'ldp:contains': super().to_representation(value), - 'permissions': Model.get_permissions(model, self.context['request'].user, ['view', 'add']) + 'ldp:contains': super().to_representation(filtered_values), + 'permissions': container_permissions } def get_attribute(self, instance): @@ -225,7 +250,7 @@ class LDPSerializer(HyperlinkedModelSerializer): data = super().to_representation(obj) slug_field = Model.slug_field(obj) for field in data: - if isinstance(data[field], dict) and'@id' in data[field]: + if isinstance(data[field], dict) and '@id' in data[field]: data[field]['@id'] = data[field]['@id'].format(Model.container_id(obj), str(getattr(obj, slug_field))) rdf_type = Model.get_meta(obj, 'rdf_type', None) rdf_context = Model.get_meta(obj, 'rdf_context', None) @@ -252,7 +277,8 @@ class LDPSerializer(HyperlinkedModelSerializer): serializer_generator = LDPViewSet(model=model_class, lookup_field=Model.get_meta(model_class, 'lookup_field', 'pk'), permission_classes=Model.get_meta(model_class, - 'permission_classes', [LDPPermissions]), + 'permission_classes', + [LDPPermissions]), fields=Model.get_meta(model_class, 'serializer_fields', []), nested_fields=Model.get_meta(model_class, 'nested_fields', [])) parent_depth = max(getattr(self.parent.Meta, "depth", 0) - 1, 0) @@ -388,7 +414,6 @@ class LDPSerializer(HyperlinkedModelSerializer): kwargs['required'] = False return NestedLDPSerializer, kwargs - @classmethod def many_init(cls, *args, **kwargs): kwargs['child'] = cls(**kwargs) diff --git a/djangoldp/tests/__init__.py b/djangoldp/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/djangoldp/tests/tests_temp.py b/djangoldp/tests/tests_temp.py index 1864f386b785ba0ca266a74422bd152cb91c1533..25e52460d869377451b6b300202b35e62d8bc0a4 100644 --- a/djangoldp/tests/tests_temp.py +++ b/djangoldp/tests/tests_temp.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import User from django.test import TestCase from rest_framework.test import APIRequestFactory, APIClient -from djangoldp.tests.models import Skill, JobOffer +from djangoldp.tests.models import Skill, JobOffer, Post class TestTemp(TestCase): @@ -18,3 +18,4 @@ class TestTemp(TestCase): pass +