Skip to content
Snippets Groups Projects
Commit 76f39ef2 authored by Valentin Rigal's avatar Valentin Rigal
Browse files

Implement the force approach

parent 740d8999
No related branches found
No related tags found
No related merge requests found
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.db.models import IntegerField, Q, Value, functions
from django.db.models import Case, IntegerField, Q, Value, When, functions
from django.db.models.query_utils import DeferredAttribute
from django.shortcuts import get_object_or_404
from django.views.decorators.cache import cache_page
......@@ -44,7 +44,7 @@ class ACLMixin(object):
.filter(public=True) \
.annotate(max_level=Value(default_level, IntegerField()))
def rights_filter(self, model, level, public=False):
def rights_filter(self, model, level):
"""
Return a model queryset matching a given access level for this user.
"""
......@@ -64,33 +64,52 @@ class ACLMixin(object):
.annotate(max_level=Value(Role.Admin.value, IntegerField())) \
.order_by(*self.mixin_order_by_fields, 'id')
# Filter users rights and annotate the resulting level for those rights
queryset = model.objects \
.filter(
# Filter instances with rights concerning this user. This may create duplicates
Q(memberships__user=self.user)
| Q(memberships__group__memberships__user=self.user)
) \
.annotate(
# Keep only the lowest level for each right via group
max_level=functions.Least(
'memberships__level',
# In case of direct right, the group level will be skipped (Null value)
'memberships__group__memberships__level'
)
qs_filters = (
# Filter corpora with a right (direct or via a group)
Q(memberships__user=self.user)
# Note: in case of direct right, the group level will be skipped (Null value)
| Q(memberships__group__memberships__user=self.user)
)
if include_public:
# Also match public corpora for which this user has no right
qs_filters |= Q(
Q(public=True)
& ~Q(memberships__user=self.user)
& ~Q(memberships__group__memberships__user=self.user)
)
# Order by decreasing max_level to make sure we keep the max among all rights
queryset = queryset.filter(max_level__gte=level) \
.order_by(*self.mixin_order_by_fields, 'id', '-max_level') \
.distinct(*self.mixin_order_by_fields, 'id')
# Use a join to add public instances as this is the more elegant solution
# Annotate instances for each right. This may return duplicated instances
max_level_annotation = functions.Least(
'memberships__level',
# In case of direct right, group level will be skipped (Null value)
'memberships__group__memberships__level'
)
if include_public:
queryset = queryset.union(self.get_public_instances(model, Role.Guest.value))
# Also annotate public instances with no member right
max_level_annotation = Case(
When(
Q(
Q(public=True)
& ~Q(memberships__user=self.user)
& ~Q(memberships__group__memberships__user=self.user)
),
# Set the rôle to guest on public instances
then=Value(Role.Guest.value)
),
default=max_level_annotation,
output_field=IntegerField()
)
# Return distinct corpus with the max right level among matching rights
return queryset.order_by(*self.mixin_order_by_fields, 'id')
return model.objects \
.filter(qs_filters) \
.annotate(max_level=max_level_annotation) \
.filter(max_level__gte=level) \
.order_by(
# Order by decreasing max_level to make sure we keep the max among all rights
*self.mixin_order_by_fields, 'id', '-max_level'
) \
.distinct(*self.mixin_order_by_fields, 'id')
def has_access(self, instance, level):
self._check_level(level)
......
......@@ -15,4 +15,5 @@ LEFT OUTER JOIN "users_right" T5 ON ("users_group"."id" = T5."content_id"
WHERE (("users_right"."user_id" = {user_id}
OR T5."user_id" = {user_id})
AND LEAST("users_right"."level", T5."level") >= {level})
ORDER BY "documents_corpus"."id" ASC
ORDER BY "documents_corpus"."id" ASC,
"max_level" DESC
(SELECT DISTINCT ON ("documents_corpus"."id") "documents_corpus"."created",
"documents_corpus"."updated",
"documents_corpus"."id",
"documents_corpus"."name",
"documents_corpus"."description",
"documents_corpus"."repository_id",
"documents_corpus"."public",
LEAST("users_right"."level", T5."level") AS "max_level"
FROM "documents_corpus"
INNER JOIN "users_right" ON ("documents_corpus"."id" = "users_right"."content_id"
SELECT DISTINCT ON ("documents_corpus"."id") "documents_corpus"."created",
"documents_corpus"."updated",
"documents_corpus"."id",
"documents_corpus"."name",
"documents_corpus"."description",
"documents_corpus"."repository_id",
"documents_corpus"."public",
CASE
WHEN ("documents_corpus"."public"
AND NOT ("users_right"."user_id" = {user_id}
AND "users_right"."user_id" IS NOT NULL)
AND NOT (T5."user_id" = {user_id}
AND T5."user_id" IS NOT NULL)) THEN 10
ELSE LEAST("users_right"."level", T5."level")
END AS "max_level"
FROM "documents_corpus"
LEFT OUTER JOIN "users_right" ON ("documents_corpus"."id" = "users_right"."content_id"
AND ("users_right"."content_type_id" = {corpus_type_id}))
LEFT OUTER JOIN "users_group" ON ("users_right"."group_id" = "users_group"."id")
LEFT OUTER JOIN "users_right" T5 ON ("users_group"."id" = T5."content_id"
AND (T5."content_type_id" = {group_type_id}))
WHERE (("users_right"."user_id" = {user_id}
OR T5."user_id" = {user_id})
AND LEAST("users_right"."level", T5."level") >= {level})
ORDER BY "documents_corpus"."id" ASC, "max_level" DESC)
UNION
(SELECT "documents_corpus"."created",
"documents_corpus"."updated",
"documents_corpus"."id",
"documents_corpus"."name",
"documents_corpus"."description",
"documents_corpus"."repository_id",
"documents_corpus"."public",
10 AS "max_level"
FROM "documents_corpus"
WHERE "documents_corpus"."public")
ORDER BY (3) ASC
LEFT OUTER JOIN "users_group" ON ("users_right"."group_id" = "users_group"."id")
LEFT OUTER JOIN "users_right" T5 ON ("users_group"."id" = T5."content_id"
AND (T5."content_type_id" = {group_type_id}))
WHERE (("users_right"."user_id" = {user_id}
OR T5."user_id" = {user_id}
OR ("documents_corpus"."public"
AND NOT ("documents_corpus"."id" IN
(SELECT U1."content_id"
FROM "users_right" U1
WHERE (U1."user_id" = {user_id}
AND U1."content_type_id" = {corpus_type_id}
AND U1."id" = "users_right"."id")))
AND NOT ("documents_corpus"."id" IN
(SELECT U1."content_id"
FROM "users_right" U1
INNER JOIN "users_group" U2 ON (U1."group_id" = U2."id")
INNER JOIN "users_right" U3 ON (U2."id" = U3."content_id"
AND (U3."content_type_id" = {group_type_id}))
WHERE (U3."user_id" = {user_id}
AND U1."content_type_id" = {corpus_type_id}
AND U1."id" = "users_right"."id")))))
AND CASE
WHEN ("documents_corpus"."public"
AND NOT ("users_right"."user_id" = {user_id}
AND "users_right"."user_id" IS NOT NULL)
AND NOT (T5."user_id" = {user_id}
AND T5."user_id" IS NOT NULL)) THEN 10
ELSE LEAST("users_right"."level", T5."level")
END >= 10)
ORDER BY "documents_corpus"."id" ASC,
"max_level" DESC
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment