Commit 0b630a1d authored by Jean-Baptiste Pasquier's avatar Jean-Baptiste Pasquier

Merge branch '161-filter-container-by-permission' into 'master'

Resolve "Filter every container by permission on each object contained"

Closes #161

See merge request !88
parents 54fdc333 8ab9cd8f
Pipeline #4927 passed with stage
in 1 minute and 21 seconds
......@@ -74,6 +74,12 @@ class Model(models.Model):
view, args, kwargs = get_resolver().resolve(id)
return view.initkwargs['model'].objects.get(**kwargs)
def resolve_parent(cls, path):
split = path.strip('/').split('/')
parent_path = "/".join(split[0:len(split) - 1])
return Model.resolve_id(parent_path)
def resolve_container(cls, path):
path = cls.__clean_path(path)
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
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
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')) ==
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')) ==
return owner_perms
......@@ -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
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):
obj = Model.resolve_parent(request.path)
model = view.parent_model
except Resolver404:
obj = None
model = view.model
obj = Model.resolve_id(request._request.path)
model = view.model
obj = view.model.resolve_id(request._request.path)
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
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
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'])
parent_model = Model.resolve_parent(self.context['request'].path)
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'])
Model.get_permissions(parent_model, self.context['request'].user,
return {'@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', [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
def many_init(cls, *args, **kwargs):
kwargs['child'] = cls(**kwargs)
......@@ -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):
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment