Filtering for sandbox (#4216)

* Implemented visibility for org= as { 'organization': None }

Now we have 3 types of visibility (aka org_filter)
1. org=slug - see objects only for the organization
2. org=     - see objects only for sandbox (organzation is None)
3. None     - see all objects which you can access
main
Nikita Manovich 4 years ago committed by GitHub
parent f831142b5e
commit 5ccc596888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -73,6 +73,7 @@ from cvat.apps.iam.permissions import (CloudStoragePermission,
class ServerViewSet(viewsets.ViewSet): class ServerViewSet(viewsets.ViewSet):
serializer_class = None serializer_class = None
iam_organization_field = None
# To get nice documentation about ServerViewSet actions it is necessary # To get nice documentation about ServerViewSet actions it is necessary
# to implement the method. By default, ViewSet doesn't provide it. # to implement the method. By default, ViewSet doesn't provide it.
@ -245,6 +246,7 @@ class ProjectViewSet(viewsets.ModelViewSet):
ordering_fields = ("id", "name", "owner", "status", "assignee") ordering_fields = ("id", "name", "owner", "status", "assignee")
ordering = ("-id",) ordering = ("-id",)
http_method_names = ('get', 'post', 'head', 'patch', 'delete') http_method_names = ('get', 'post', 'head', 'patch', 'delete')
iam_organization_field = 'organization'
def get_serializer_class(self): def get_serializer_class(self):
if self.request.path.endswith('tasks'): if self.request.path.endswith('tasks'):
@ -557,6 +559,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
search_fields = ("name", "owner__username", "mode", "status") search_fields = ("name", "owner__username", "mode", "status")
filterset_class = TaskFilter filterset_class = TaskFilter
ordering_fields = ("id", "name", "owner", "status", "assignee", "subset") ordering_fields = ("id", "name", "owner", "status", "assignee", "subset")
iam_organization_field = 'organization'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -589,6 +592,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
if instance.project: if instance.project:
db_project = instance.project db_project = instance.project
db_project.save() db_project.save()
assert instance.organization == db_project.organization
def perform_destroy(self, instance): def perform_destroy(self, instance):
task_dirname = instance.get_task_dirname() task_dirname = instance.get_task_dirname()
@ -882,6 +886,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
class JobViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, class JobViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
mixins.RetrieveModelMixin, mixins.UpdateModelMixin): mixins.RetrieveModelMixin, mixins.UpdateModelMixin):
queryset = Job.objects.all().order_by('id') queryset = Job.objects.all().order_by('id')
iam_organization_field = 'segment__task__organization'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -986,6 +991,7 @@ class JobViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
class IssueViewSet(viewsets.ModelViewSet): class IssueViewSet(viewsets.ModelViewSet):
queryset = Issue.objects.all().order_by('-id') queryset = Issue.objects.all().order_by('-id')
http_method_names = ['get', 'post', 'patch', 'delete', 'options'] http_method_names = ['get', 'post', 'patch', 'delete', 'options']
iam_organization_field = 'job__segment__task__organization'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -1020,6 +1026,7 @@ class IssueViewSet(viewsets.ModelViewSet):
class CommentViewSet(viewsets.ModelViewSet): class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all().order_by('-id') queryset = Comment.objects.all().order_by('-id')
http_method_names = ['get', 'post', 'patch', 'delete', 'options'] http_method_names = ['get', 'post', 'patch', 'delete', 'options']
iam_organization_field = 'issue__job__segment__task__organization'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -1061,6 +1068,7 @@ class UserViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
http_method_names = ['get', 'post', 'head', 'patch', 'delete'] http_method_names = ['get', 'post', 'head', 'patch', 'delete']
search_fields = ('username', 'first_name', 'last_name') search_fields = ('username', 'first_name', 'last_name')
filterset_class = UserFilter filterset_class = UserFilter
iam_organization_field = 'memberships__organization'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -1154,6 +1162,7 @@ class CloudStorageViewSet(viewsets.ModelViewSet):
queryset = CloudStorageModel.objects.all().prefetch_related('data').order_by('-id') queryset = CloudStorageModel.objects.all().prefetch_related('data').order_by('-id')
search_fields = ('provider_type', 'display_name', 'resource', 'credentials_type', 'owner__username', 'description') search_fields = ('provider_type', 'display_name', 'resource', 'credentials_type', 'owner__username', 'description')
filterset_class = CloudStorageFilter filterset_class = CloudStorageFilter
iam_organization_field = 'organization'
def get_serializer_class(self): def get_serializer_class(self):
if self.request.method in ("POST", "PATCH"): if self.request.method in ("POST", "PATCH"):

@ -3,7 +3,6 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import coreapi import coreapi
from django.core.exceptions import FieldError
from rest_framework.filters import BaseFilterBackend from rest_framework.filters import BaseFilterBackend
class OrganizationFilterBackend(BaseFilterBackend): class OrganizationFilterBackend(BaseFilterBackend):
@ -20,11 +19,9 @@ class OrganizationFilterBackend(BaseFilterBackend):
# filter isn't necessary but it is an extra check that we show only # filter isn't necessary but it is an extra check that we show only
# objects inside an organization if the request in context of the # objects inside an organization if the request in context of the
# organization. # organization.
try: visibility = request.iam_context['visibility']
organization = request.iam_context['organization'] if visibility and view.iam_organization_field:
if organization: visibility[view.iam_organization_field] = visibility.pop('organization')
return queryset.filter(organization=organization) return queryset.filter(**visibility).distinct()
else: else:
return queryset
except FieldError:
return queryset return queryset

@ -32,22 +32,27 @@ def get_context(request):
org_id = request.GET.get('org_id') org_id = request.GET.get('org_id')
org_header = request.headers.get('X-Organization') org_header = request.headers.get('X-Organization')
if org_id and (org_slug or org_header): if org_id != None and (org_slug != None or org_header != None):
raise BadRequest('You cannot specify "org_id" query parameter with ' + raise BadRequest('You cannot specify "org_id" query parameter with ' +
'"org" query parameter or "X-Organization" HTTP header at the same time.') '"org" query parameter or "X-Organization" HTTP header at the same time.')
if org_slug and org_header and org_slug != org_header: if org_slug != None and org_header != None and org_slug != org_header:
raise BadRequest('You cannot specify "org" query parameter and ' + raise BadRequest('You cannot specify "org" query parameter and ' +
'"X-Organization" HTTP header with different values.') '"X-Organization" HTTP header with different values.')
org_slug = org_slug or org_header org_slug = org_slug if org_slug != None else org_header
org_filter = None
if org_slug: if org_slug:
organization = Organization.objects.get(slug=org_slug) organization = Organization.objects.get(slug=org_slug)
membership = Membership.objects.filter(organization=organization, membership = Membership.objects.filter(organization=organization,
user=request.user).first() user=request.user).first()
org_filter = { 'organization': organization.id }
elif org_id: elif org_id:
organization = Organization.objects.get(id=int(org_id)) organization = Organization.objects.get(id=int(org_id))
membership = Membership.objects.filter(organization=organization, membership = Membership.objects.filter(organization=organization,
user=request.user).first() user=request.user).first()
org_filter = { 'organization': organization.id }
elif org_slug is not None:
org_filter = { 'organization': None }
except Organization.DoesNotExist: except Organization.DoesNotExist:
raise BadRequest(f'{org_slug or org_id} organization does not exist.') raise BadRequest(f'{org_slug or org_id} organization does not exist.')
@ -58,6 +63,7 @@ def get_context(request):
"privilege": groups[0] if groups else None, "privilege": groups[0] if groups else None,
"membership": membership, "membership": membership,
"organization": organization, "organization": organization,
"visibility": org_filter,
} }
return context return context

@ -555,6 +555,7 @@ def return_response(success_code=status.HTTP_200_OK):
class FunctionViewSet(viewsets.ViewSet): class FunctionViewSet(viewsets.ViewSet):
lookup_value_regex = '[a-zA-Z0-9_.-]+' lookup_value_regex = '[a-zA-Z0-9_.-]+'
lookup_field = 'func_id' lookup_field = 'func_id'
iam_organization_field = None
@return_response() @return_response()
def list(self, request): def list(self, request):
@ -585,6 +586,8 @@ class FunctionViewSet(viewsets.ViewSet):
return lambda_func.invoke(db_task, request.data) return lambda_func.invoke(db_task, request.data)
class RequestViewSet(viewsets.ViewSet): class RequestViewSet(viewsets.ViewSet):
iam_organization_field = None
@return_response() @return_response()
def list(self, request): def list(self, request):
queue = LambdaQueue() queue = LambdaQueue()

@ -21,6 +21,7 @@ class OrganizationViewSet(viewsets.ModelViewSet):
ordering = ['-id'] ordering = ['-id']
http_method_names = ['get', 'post', 'patch', 'delete', 'head', 'options'] http_method_names = ['get', 'post', 'patch', 'delete', 'head', 'options']
pagination_class = None pagination_class = None
iam_organization_field = None
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -52,6 +53,7 @@ class MembershipViewSet(mixins.RetrieveModelMixin, mixins.DestroyModelMixin,
ordering = ['-id'] ordering = ['-id']
http_method_names = ['get', 'patch', 'delete', 'head', 'options'] http_method_names = ['get', 'patch', 'delete', 'head', 'options']
filterset_class = MembershipFilter filterset_class = MembershipFilter
iam_organization_field = 'organization'
def get_serializer_class(self): def get_serializer_class(self):
if self.request.method in SAFE_METHODS: if self.request.method in SAFE_METHODS:
@ -68,6 +70,7 @@ class InvitationViewSet(viewsets.ModelViewSet):
queryset = Invitation.objects.all() queryset = Invitation.objects.all()
ordering = ['-created_date'] ordering = ['-created_date']
http_method_names = ['get', 'post', 'patch', 'delete', 'head', 'options'] http_method_names = ['get', 'post', 'patch', 'delete', 'head', 'options']
iam_organization_field = 'membership__organization'
def get_serializer_class(self): def get_serializer_class(self):
if self.request.method in SAFE_METHODS: if self.request.method in SAFE_METHODS:

@ -16,6 +16,7 @@ class RestrictionsViewSet(viewsets.ViewSet):
serializer_class = None serializer_class = None
permission_classes = [AllowAny] permission_classes = [AllowAny]
authentication_classes = [] authentication_classes = []
iam_organization_field = None
# To get nice documentation about ServerViewSet actions it is necessary # To get nice documentation about ServerViewSet actions it is necessary
# to implement the method. By default, ViewSet doesn't provide it. # to implement the method. By default, ViewSet doesn't provide it.

Loading…
Cancel
Save