Skip to content

Commit

Permalink
feat: OPTIC-630: Restrict access to project import UI and API through…
Browse files Browse the repository at this point in the history
… a billing flag (HumanSignal#5808)

**Reason for change**

To enable enterprise in ensuring security compliance for data sensitive
UI + APIs.

---------

Co-authored-by: robot-ci-heartex <[email protected]>
  • Loading branch information
bmartel and robot-ci-heartex authored May 6, 2024
1 parent 81fc6e1 commit 4cb2e40
Show file tree
Hide file tree
Showing 10 changed files with 42 additions and 21 deletions.
1 change: 1 addition & 0 deletions label_studio/core/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@
PREPROCESS_FIELD_NAME = 'data_manager.functions.preprocess_field_name'
INTERACTIVE_DATA_SERIALIZER = 'data_export.serializers.BaseExportDataSerializerForInteractive'
STORAGE_PERMISSION = 'io_storages.permissions.StoragePermission'
PROJECT_IMPORT_PERMISSION = 'projects.permissions.ProjectImportPermission'
DELETE_TASKS_ANNOTATIONS_POSTPROCESS = None


Expand Down
6 changes: 6 additions & 0 deletions label_studio/data_import/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView
from tasks.functions import update_tasks_counters
from tasks.models import Prediction, Task
from users.models import User
from webhooks.models import WebhookAction
from webhooks.utils import emit_webhooks_for_instance

from label_studio.core.utils.common import load_func

from .functions import (
async_import_background,
async_reimport_background,
Expand All @@ -49,6 +52,8 @@

logger = logging.getLogger(__name__)

ProjectImportPermission = load_func(settings.PROJECT_IMPORT_PERMISSION)


task_create_response_scheme = {
201: openapi.Response(
Expand Down Expand Up @@ -175,6 +180,7 @@
# Import
class ImportAPI(generics.CreateAPIView):
permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [ProjectImportPermission]
parser_classes = (JSONParser, MultiPartParser, FormParser)
serializer_class = ImportApiSerializer
queryset = Task.objects.all()
Expand Down
15 changes: 9 additions & 6 deletions label_studio/projects/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import exception_handler
from tasks.models import Task
from tasks.serializers import (
Expand All @@ -54,8 +55,11 @@
from webhooks.models import WebhookAction
from webhooks.utils import api_webhook, api_webhook_for_delete, emit_webhooks_for_instance

from label_studio.core.utils.common import load_func

logger = logging.getLogger(__name__)

ProjectImportPermission = load_func(settings.PROJECT_IMPORT_PERMISSION)

_result_schema = openapi.Schema(
title='Labeling result',
Expand Down Expand Up @@ -207,7 +211,6 @@ def post(self, request, *args, **kwargs):
),
)
class ProjectAPI(generics.RetrieveUpdateDestroyAPIView):

parser_classes = (JSONParser, FormParser, MultiPartParser)
queryset = Project.objects.with_counts()
permission_required = ViewClassPermission(
Expand Down Expand Up @@ -275,11 +278,10 @@ def put(self, request, *args, **kwargs):
),
) # leaving this method decorator info in case we put it back in swagger API docs
class ProjectNextTaskAPI(generics.RetrieveAPIView):

permission_required = all_permissions.tasks_view
serializer_class = TaskWithAnnotationsAndPredictionsAndDraftsSerializer # using it for swagger API docs
queryset = Project.objects.all()
swagger_schema = None # this endpoint doesn't need to be in swagger API docs
swagger_schema = None # this endpoint doesn't need to be in swagger API docs

def get(self, request, *args, **kwargs):
project = self.get_object()
Expand Down Expand Up @@ -444,9 +446,10 @@ def post(self, *args, **kwargs):
),
)
class ProjectImportAPI(generics.RetrieveAPIView):
permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [ProjectImportPermission]
parser_classes = (JSONParser,)
serializer_class = ProjectImportSerializer
permission_required = all_permissions.projects_change
queryset = ProjectImport.objects.all()
lookup_url_kwarg = 'import_pk'

Expand All @@ -468,9 +471,10 @@ class ProjectImportAPI(generics.RetrieveAPIView):
),
)
class ProjectReimportAPI(generics.RetrieveAPIView):
permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [ProjectImportPermission]
parser_classes = (JSONParser,)
serializer_class = ProjectReimportSerializer
permission_required = all_permissions.projects_change
queryset = ProjectReimport.objects.all()
lookup_url_kwarg = 'reimport_pk'

Expand Down Expand Up @@ -516,7 +520,6 @@ class ProjectReimportAPI(generics.RetrieveAPIView):
),
)
class ProjectTaskListAPI(GetParentObjectMixin, generics.ListCreateAPIView, generics.DestroyAPIView):

parser_classes = (JSONParser, FormParser)
queryset = Task.objects.all()
parent_queryset = Project.objects.all()
Expand Down
11 changes: 11 additions & 0 deletions label_studio/projects/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from rest_framework.permissions import BasePermission


class ProjectImportPermission(BasePermission):
"""
Checks if the user has access to the project import API
Default case is always true
"""

def has_permission(self, request, view):
return True
8 changes: 4 additions & 4 deletions web/dist/apps/labelstudio/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"message": "Merge branch 'fb-optic-669' of github.com:heartexlabs/label-studio into fb-optic-669",
"commit": "badd45f7edecac446c7592e38dddcba33fc5a14b",
"date": "2024-05-06T14:31:18.000Z",
"branch": "fb-optic-669"
"message": "Merge branch 'develop' into fb-optic-630",
"commit": "d33a2fe8bd78ab352d6d1ab88738c07f9d0bb2a0",
"date": "2024-05-06T19:55:41.000Z",
"branch": "fb-optic-630"
}
2 changes: 1 addition & 1 deletion web/dist/libs/datamanager/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dist/libs/datamanager/main.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions web/dist/libs/datamanager/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"message": "Merge branch 'fb-optic-669' of github.com:heartexlabs/label-studio into fb-optic-669",
"commit": "badd45f7edecac446c7592e38dddcba33fc5a14b",
"date": "2024-05-06T14:31:18.000Z",
"branch": "fb-optic-669"
"message": "Merge branch 'develop' into fb-optic-630",
"commit": "d33a2fe8bd78ab352d6d1ab88738c07f9d0bb2a0",
"date": "2024-05-06T19:55:41.000Z",
"branch": "fb-optic-630"
}
8 changes: 4 additions & 4 deletions web/dist/libs/editor/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"message": "Merge branch 'fb-optic-669' of github.com:heartexlabs/label-studio into fb-optic-669",
"commit": "badd45f7edecac446c7592e38dddcba33fc5a14b",
"date": "2024-05-06T14:31:18.000Z",
"branch": "fb-optic-669"
"message": "Merge branch 'develop' into fb-optic-630",
"commit": "d33a2fe8bd78ab352d6d1ab88738c07f9d0bb2a0",
"date": "2024-05-06T19:55:41.000Z",
"branch": "fb-optic-630"
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export const DataView = injector(
"Looks like you have not imported any data yet"
)}
</Elem>
{!hasData && (
{!hasData && !!store.interfaces.get("import") && (
<Elem name="navigation">
<ImportButton look="primary" href="./import">
Go to import
Expand Down

0 comments on commit 4cb2e40

Please sign in to comment.