diff --git a/ocl/collection/filters.py b/ocl/collection/filters.py index 2d388bc..20cc2db 100644 --- a/ocl/collection/filters.py +++ b/ocl/collection/filters.py @@ -1,15 +1,7 @@ __author__ = 'snyaggarwal' -from oclapi.filters import HaystackSearchFilter +from oclapi.filters import ConceptContainerPermissionedSearchFilter -class CollectionSearchFilter(HaystackSearchFilter): - def get_filters(self, request, view): - filters = super(CollectionSearchFilter, self).get_filters(request, view) - if view.parent_resource: - filters.update({'owner': view.parent_resource.mnemonic}) - filters.update({'ownerType': view.parent_resource.resource_type()}) - else: - filters.update({'public_can_view': True}) - - return filters +class CollectionSearchFilter(ConceptContainerPermissionedSearchFilter): + pass diff --git a/ocl/collection/views.py b/ocl/collection/views.py index 09b62f2..a5a07db 100644 --- a/ocl/collection/views.py +++ b/ocl/collection/views.py @@ -1,6 +1,8 @@ import logging from django.conf import settings +from django.contrib.contenttypes.models import ContentType from django.db import IntegrityError +from django.db.models import Q from collection.validation_messages import HEAD_OF_CONCEPT_ADDED_TO_COLLECTION, CONCEPT_ADDED_TO_COLLECTION_FMT, \ HEAD_OF_MAPPING_ADDED_TO_COLLECTION, MAPPING_ADDED_TO_COLLECTION_FMT @@ -9,23 +11,17 @@ CollectionVersionListSerializer, CollectionVersionCreateSerializer, CollectionVersionDetailSerializer, \ CollectionVersionUpdateSerializer, \ CollectionReferenceSerializer -from concepts.models import Concept, ConceptVersion -from mappings.models import MappingVersion -from sources.models import SourceVersion -from concepts.serializers import ConceptListSerializer +from concepts.models import Concept from django.http import HttpResponse, HttpResponseForbidden -from mappings.models import Mapping -from mappings.serializers import MappingDetailSerializer from oclapi.mixins import ListWithHeadersMixin from oclapi.permissions import CanViewConceptDictionary, CanEditConceptDictionary, CanViewConceptDictionaryVersion, \ CanEditConceptDictionaryVersion, HasOwnership from oclapi.permissions import HasAccessToVersionedObject from oclapi.views import ResourceVersionMixin, ResourceAttributeChildMixin, ConceptDictionaryUpdateMixin, \ - ConceptDictionaryCreateMixin, ConceptDictionaryExtrasView, ConceptDictionaryExtraRetrieveUpdateDestroyView, \ - BaseAPIView -from oclapi.models import ACCESS_TYPE_EDIT, ACCESS_TYPE_VIEW + ConceptDictionaryCreateMixin, ConceptDictionaryExtrasView, ConceptDictionaryExtraRetrieveUpdateDestroyView +from oclapi.models import ACCESS_TYPE_NONE from rest_framework import mixins, status -from rest_framework.generics import RetrieveAPIView, UpdateAPIView, get_object_or_404, DestroyAPIView +from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView from rest_framework.response import Response from users.models import UserProfile from orgs.models import Organization @@ -33,8 +29,7 @@ from celery_once import AlreadyQueued from django.shortcuts import get_list_or_404 from collection.filters import CollectionSearchFilter -from tasks import update_collection_in_solr, delete_resources_from_collection_in_solr -from django.core.exceptions import ValidationError +from tasks import delete_resources_from_collection_in_solr logger = logging.getLogger('oclapi') @@ -53,22 +48,19 @@ def get_version_detail_serializer(self, obj, data=None, files=None, partial=Fals def get_queryset(self): owner = self.get_owner() - if not self.kwargs: + if not owner: return self.queryset elif 'collection' in self.kwargs: - return Collection.objects.filter(parent_id=owner.id, mnemonic=self.kwargs['collection']) + return self.model.objects.filter( + parent_id=owner.id, + mnemonic=self.kwargs['collection'], + parent_type=ContentType.objects.get_for_model(owner), + ) else: - return self.queryset.filter(parent_id=owner.id) + return self.queryset.filter(parent_id=owner.id, parent_type=ContentType.objects.get_for_model(owner)) def get_owner(self): - owner = None - if 'user' in self.kwargs: - owner_id = self.kwargs['user'] - owner = UserProfile.objects.get(mnemonic=owner_id) - elif 'org' in self.kwargs: - owner_id = self.kwargs['org'] - owner = Organization.objects.get(mnemonic=owner_id) - return owner + return self.parent_resource class CollectionVersionBaseView(ResourceVersionMixin): @@ -291,31 +283,14 @@ def get(self, request, *args, **kwargs): self.serializer_class = CollectionDetailSerializer if self.is_verbose(request) else CollectionListSerializer self.contains_uri = request.QUERY_PARAMS.get('contains', None) self.user = request.QUERY_PARAMS.get('user', None) - # Running the filter_backends seems to reset changes made to the queryset. - # Therefore, remove the filter_backends when the 'contains' parameter is passed, and - # apply the appropriate public_access filter in get_queryset - # TODO correct the behavior of filter_backends, and remove this hack to get around it - if self.contains_uri != None: - self.filter_backends=[] - if self.user != None: - self.filter_backends=[] collection_list = self.list(request, *args, **kwargs) return collection_list def get_queryset(self): queryset = super(CollectionListView, self).get_queryset() - # If the 'contains' parameter is used, the filter_backends have been cleared to prevent - # reset of the queryset. Therefore, add a public_access filter to the queryset. - # TODO correct the behavior of filter_backends, and remove this hack to get around it - if self.contains_uri != None: + if self.contains_uri is not None: from django_mongodb_engine.query import A - queryset = queryset.filter(references=A('expression', self.contains_uri), public_access__in=[ACCESS_TYPE_EDIT, ACCESS_TYPE_VIEW]) - if self.user: - if self.user != 'root': - from users.models import UserProfile - user_profile = UserProfile.objects.filter(mnemonic=self.user) - if user_profile: - queryset = queryset.filter(parent_id__in=[user_profile[0].id] + user_profile[0].organizations, public_access__in=[ACCESS_TYPE_EDIT, ACCESS_TYPE_VIEW]) + queryset = queryset.filter(references=A('expression', self.contains_uri)) return queryset def get_csv_rows(self, queryset=None): diff --git a/ocl/concepts/views.py b/ocl/concepts/views.py index ebddc60..c26fc9a 100644 --- a/ocl/concepts/views.py +++ b/ocl/concepts/views.py @@ -1,11 +1,10 @@ -from django.core.exceptions import ValidationError from django.db.models import Q from django.db.models.query import EmptyQuerySet from django.http import Http404 from django.views.decorators.csrf import csrf_exempt from rest_framework import mixins, status from rest_framework.generics import (RetrieveAPIView, get_object_or_404, UpdateAPIView, - DestroyAPIView, RetrieveUpdateDestroyAPIView, CreateAPIView, + DestroyAPIView, RetrieveUpdateDestroyAPIView, ListCreateAPIView, ListAPIView) from rest_framework.response import Response from concepts.filters import LimitSourceVersionFilter, PublicConceptsSearchFilter, LimitCollectionVersionFilter diff --git a/ocl/oclapi/filters.py b/ocl/oclapi/filters.py index 3603f68..1216916 100644 --- a/ocl/oclapi/filters.py +++ b/ocl/oclapi/filters.py @@ -1,8 +1,14 @@ from django.conf import settings +from django.contrib.contenttypes.models import ContentType +from django.db.models import Q from haystack.inputs import Raw -from haystack.query import RelatedSearchQuerySet, SearchQuerySet +from haystack.query import RelatedSearchQuerySet from rest_framework.filters import BaseFilterBackend +from oclapi.models import ACCESS_TYPE_NONE +from orgs.models import Organization +from users.models import UserProfile + class SearchQuerySetWrapper(object): @@ -128,7 +134,7 @@ def _filter_queryset(self, request, queryset, view, sqs): sqs = sqs.order_by(default_sort) sqs = sqs.models(view.model) if hasattr(sqs, 'load_all_queryset'): - sqs = sqs.load_all_queryset(view.model, queryset) + sqs = sqs.load_all().load_all_queryset(view.model, queryset) return SearchQuerySetWrapper(sqs) if hasattr(view, 'default_order_by'): @@ -138,4 +144,18 @@ def _filter_queryset(self, request, queryset, view, sqs): class HaystackSearchFilter(BaseHaystackSearchFilter): def filter_queryset(self, request, queryset, view): - return self._filter_queryset(request, queryset, view, SearchQuerySet()) \ No newline at end of file + return self._filter_queryset(request, queryset, view, RelatedSearchQuerySet()) + + +class ConceptContainerPermissionedSearchFilter(HaystackSearchFilter): + def filter_queryset(self, request, queryset, view): + current_user = request.user + permissioned_queryset = queryset.filter(~Q(public_access=ACCESS_TYPE_NONE)) + if not current_user.is_anonymous(): + user_profile = UserProfile.objects.get(user=current_user) + permissioned_queryset |= queryset.filter( + Q(parent_id=user_profile.id, parent_type=ContentType.objects.get_for_model(UserProfile)) | + Q(parent_id__in=user_profile.organizations, parent_type=ContentType.objects.get_for_model(Organization)) + ) + + return super(ConceptContainerPermissionedSearchFilter, self).filter_queryset(request, permissioned_queryset, view) diff --git a/ocl/sources/filters.py b/ocl/sources/filters.py index 58e04c7..ccb93e8 100644 --- a/ocl/sources/filters.py +++ b/ocl/sources/filters.py @@ -1,16 +1,7 @@ -__author__ = 'misternando' - -from oclapi.filters import HaystackSearchFilter +from oclapi.filters import ConceptContainerPermissionedSearchFilter __author__ = 'misternando' -class SourceSearchFilter(HaystackSearchFilter): - def get_filters(self, request, view): - filters = super(SourceSearchFilter, self).get_filters(request, view) - if view.parent_resource: - filters.update({'owner': view.parent_resource.mnemonic}) - filters.update({'ownerType': view.parent_resource.resource_type()}) - else: - filters.update({'public_can_view': True}) - return filters +class SourceSearchFilter(ConceptContainerPermissionedSearchFilter): + pass diff --git a/ocl/sources/views.py b/ocl/sources/views.py index d6e8ade..53bf376 100644 --- a/ocl/sources/views.py +++ b/ocl/sources/views.py @@ -1,33 +1,26 @@ import logging from django.conf import settings +from django.contrib.contenttypes.models import ContentType from django.db import IntegrityError from django.db.models import Q -from django.db.models.query import EmptyQuerySet from django.http import HttpResponse, HttpResponseForbidden from rest_framework import mixins, status -from rest_framework.generics import RetrieveAPIView, UpdateAPIView, get_object_or_404, DestroyAPIView +from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView from django.shortcuts import get_list_or_404 from rest_framework.response import Response -from concepts.models import ConceptVersion, Concept -from mappings.models import Mapping -from collection.models import CollectionVersion -from concepts.serializers import ConceptVersionDetailSerializer -from mappings.models import MappingVersion -from mappings.serializers import MappingVersionDetailSerializer from oclapi.mixins import ListWithHeadersMixin +from oclapi.models import ACCESS_TYPE_NONE from oclapi.permissions import HasAccessToVersionedObject, CanEditConceptDictionaryVersion, CanViewConceptDictionary, \ CanViewConceptDictionaryVersion, CanEditConceptDictionary, HasOwnership from oclapi.views import ResourceVersionMixin, ResourceAttributeChildMixin, ConceptDictionaryUpdateMixin, ConceptDictionaryCreateMixin, ConceptDictionaryExtrasView, ConceptDictionaryExtraRetrieveUpdateDestroyView, parse_updated_since_param, parse_boolean_query_param from sources.filters import SourceSearchFilter from sources.models import Source, SourceVersion -from oclapi.rawqueries import RawQueries from sources.serializers import SourceCreateSerializer, SourceListSerializer, SourceDetailSerializer, SourceVersionDetailSerializer, SourceVersionListSerializer, SourceVersionCreateSerializer, SourceVersionUpdateSerializer from tasks import export_source from celery_once import AlreadyQueued from users.models import UserProfile from orgs.models import Organization -from django.db.models import Q INCLUDE_CONCEPTS_PARAM = 'includeConcepts' INCLUDE_MAPPINGS_PARAM = 'includeMappings' @@ -49,6 +42,22 @@ class SourceBaseView(): def get_detail_serializer(self, obj, data=None, files=None, partial=False): return SourceDetailSerializer(obj, data, files, partial) + def get_queryset(self): + owner = self.get_owner() + if not owner: + return self.queryset + elif 'source' in self.kwargs: + return self.model.objects.filter( + parent_id=owner.id, + mnemonic=self.kwargs['source'], + parent_type=ContentType.objects.get_for_model(owner), + ) + else: + return self.queryset.filter(parent_id=owner.id, parent_type=ContentType.objects.get_for_model(owner)) + + def get_owner(self): + return self.parent_resource + class SourceRetrieveUpdateDestroyView(SourceBaseView, ConceptDictionaryUpdateMixin,