From 4cf9ed9d26b33ab7b9d098a6e52f891ea7087d15 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 12 Dec 2022 22:34:38 -0800 Subject: [PATCH] Support owner and object permissions for advanced queries --- src/documents/index.py | 23 ++++++++++++++++++++++- src/documents/serialisers.py | 2 +- src/documents/views.py | 12 +++++++++--- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/documents/index.py b/src/documents/index.py index fe40dc1a8..8a3435e3d 100644 --- a/src/documents/index.py +++ b/src/documents/index.py @@ -6,6 +6,7 @@ from contextlib import contextmanager from dateutil.parser import isoparse from django.conf import settings from documents.models import Document +from guardian.shortcuts import get_users_with_perms from whoosh import classify from whoosh import highlight from whoosh import query @@ -49,6 +50,10 @@ def get_schema(): path=TEXT(sortable=True), path_id=NUMERIC(), has_path=BOOLEAN(), + owner=TEXT(), + owner_id=NUMERIC(), + has_owner=BOOLEAN(), + viewer_id=KEYWORD(commas=True), ) @@ -90,6 +95,11 @@ def open_index_searcher(): def update_document(writer, doc): tags = ",".join([t.name for t in doc.tags.all()]) tags_ids = ",".join([str(t.id) for t in doc.tags.all()]) + users_with_perms = get_users_with_perms( + doc, + only_with_perms_in=["view_document"], + ) + viewer_ids = ",".join([str(u.id) for u in users_with_perms]) writer.update_document( id=doc.pk, title=doc.title, @@ -110,6 +120,10 @@ def update_document(writer, doc): path=doc.storage_path.name if doc.storage_path else None, path_id=doc.storage_path.id if doc.storage_path else None, has_path=doc.storage_path is not None, + owner=doc.owner.username if doc.owner else None, + owner_id=doc.owner.id if doc.owner else None, + has_owner=doc.owner is not None, + viewer_id=viewer_ids if viewer_ids else None, ) @@ -168,10 +182,17 @@ class DelayedQuery: elif k == "storage_path__isnull": criterias.append(query.Term("has_path", v == "false")) + user_criterias = [query.Term("has_owner", False)] + if "user" in self.query_params: + user_criterias.append(query.Term("owner_id", self.query_params["user"])) + user_criterias.append( + query.Term("viewer_id", str(self.query_params["user"])), + ) if len(criterias) > 0: + criterias.append(query.Or(user_criterias)) return query.And(criterias) else: - return None + return query.Or(user_criterias) def _get_query_sortedby(self): if "ordering" not in self.query_params: diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index ab7efe6de..6f4dc1ec9 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -341,7 +341,7 @@ class StoragePathField(serializers.PrimaryKeyRelatedField): return StoragePath.objects.all() -class DocumentSerializer(DynamicFieldsModelSerializer, OwnedObjectSerializer): +class DocumentSerializer(OwnedObjectSerializer, DynamicFieldsModelSerializer): correspondent = CorrespondentField(allow_null=True) tags = TagsField(many=True) diff --git a/src/documents/views.py b/src/documents/views.py index 86d607574..ac6f19c19 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -221,12 +221,12 @@ class DocumentTypeViewSet(ModelViewSet, PassUserMixin): class DocumentViewSet( + PassUserMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet, - PassUserMixin, ): model = Document queryset = Document.objects.all() @@ -256,6 +256,7 @@ class DocumentViewSet( return Document.objects.distinct() def get_serializer(self, *args, **kwargs): + super().get_serializer(*args, **kwargs) fields_param = self.request.query_params.get("fields", None) if fields_param: fields = fields_param.split(",") @@ -263,7 +264,6 @@ class DocumentViewSet( fields = None truncate_content = self.request.query_params.get("truncate_content", "False") serializer_class = self.get_serializer_class() - kwargs.setdefault("user", self.request.user) # PassUserMixin kwargs.setdefault("context", self.get_serializer_context()) kwargs.setdefault("fields", fields) kwargs.setdefault("truncate_content", truncate_content.lower() in ["true", "1"]) @@ -491,7 +491,7 @@ class DocumentViewSet( ) -class SearchResultSerializer(DocumentSerializer): +class SearchResultSerializer(DocumentSerializer, PassUserMixin): def to_representation(self, instance): doc = Document.objects.get(id=instance["id"]) r = super().to_representation(doc) @@ -527,6 +527,12 @@ class UnifiedSearchViewSet(DocumentViewSet): if self._is_search_request(): from documents import index + if hasattr(self.request, "user"): + # pass user to query for perms + self.request.query_params._mutable = True + self.request.query_params["user"] = self.request.user.id + self.request.query_params._mutable = False + if "query" in self.request.query_params: query_class = index.DelayedFullTextQuery elif "more_like_id" in self.request.query_params: