Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions label_studio/core/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class AllPermissions(BaseModel):
webhooks_change: str = 'webhooks.change'
users_token_any: str = 'users.token.any'

storages_view: str = 'storages.view'
storages_change: str = 'storages.change'
storages_sync: str = 'storages.sync'


all_permissions = AllPermissions()

Expand Down
1 change: 0 additions & 1 deletion label_studio/core/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,6 @@
TASK_SERIALIZER_BULK = 'tasks.serializers.BaseTaskSerializerBulk'
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
FEATURE_FLAGS_GET_USER_REPR = 'core.feature_flags.utils.get_user_repr'
Expand Down
8 changes: 4 additions & 4 deletions label_studio/io_storages/all_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def _get_common_storage_list():
),
)
class AllImportStorageTypesAPI(APIView):
permission_required = all_permissions.projects_change
permission_required = all_permissions.storages_view

def get(self, request, **kwargs):
return Response([{'name': s['name'], 'title': s['title']} for s in _common_storage_list])
Expand Down Expand Up @@ -104,7 +104,7 @@ def get(self, request, **kwargs):
),
)
class AllExportStorageTypesAPI(APIView):
permission_required = all_permissions.projects_change
permission_required = all_permissions.storages_view

def get(self, request, **kwargs):
return Response([{'name': s['name'], 'title': s['title']} for s in _common_storage_list])
Expand All @@ -126,7 +126,7 @@ def get(self, request, **kwargs):
)
class AllImportStorageListAPI(generics.ListAPIView):
parser_classes = (JSONParser, FormParser, MultiPartParser)
permission_required = all_permissions.projects_change
permission_required = all_permissions.storages_view

def _get_response(self, api, request, *args, **kwargs):
try:
Expand Down Expand Up @@ -164,7 +164,7 @@ def list(self, request, *args, **kwargs):
class AllExportStorageListAPI(generics.ListAPIView):

parser_classes = (JSONParser, FormParser, MultiPartParser)
permission_required = all_permissions.projects_change
permission_required = all_permissions.storages_view

def _get_response(self, api, request, *args, **kwargs):
try:
Expand Down
50 changes: 30 additions & 20 deletions label_studio/io_storages/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os
import time

from core.permissions import all_permissions
from core.permissions import ViewClassPermission, all_permissions
from core.utils.io import read_yaml
from django.conf import settings
from drf_spectacular.utils import extend_schema
Expand All @@ -15,18 +15,15 @@
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.settings import api_settings

from label_studio.core.utils.common import load_func

logger = logging.getLogger(__name__)

StoragePermission = load_func(settings.STORAGE_PERMISSION)


class ImportStorageListAPI(generics.ListCreateAPIView):
permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [StoragePermission]
permission_required = ViewClassPermission(
GET=all_permissions.storages_view,
POST=all_permissions.storages_change,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)

serializer_class = ImportStorageSerializer
Expand All @@ -46,8 +43,12 @@ def get_queryset(self):
class ImportStorageDetailAPI(generics.RetrieveUpdateDestroyAPIView):
"""RUD storage by pk specified in URL"""

permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [StoragePermission]
permission_required = ViewClassPermission(
GET=all_permissions.storages_view,
PATCH=all_permissions.storages_change,
PUT=all_permissions.storages_change,
DELETE=all_permissions.storages_change,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = ImportStorageSerializer

Expand All @@ -58,8 +59,10 @@ def put(self, request, *args, **kwargs):

class ExportStorageListAPI(generics.ListCreateAPIView):

permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [StoragePermission]
permission_required = ViewClassPermission(
GET=all_permissions.storages_view,
POST=all_permissions.storages_change,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = ExportStorageSerializer

Expand Down Expand Up @@ -91,8 +94,12 @@ def perform_create(self, serializer):
class ExportStorageDetailAPI(generics.RetrieveUpdateDestroyAPIView):
"""RUD storage by pk specified in URL"""

permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [StoragePermission]
permission_required = ViewClassPermission(
GET=all_permissions.storages_view,
PATCH=all_permissions.storages_change,
PUT=all_permissions.storages_change,
DELETE=all_permissions.storages_change,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = ExportStorageSerializer

Expand All @@ -103,7 +110,9 @@ def put(self, request, *args, **kwargs):

class ImportStorageSyncAPI(generics.GenericAPIView):

permission_required = all_permissions.projects_change
permission_required = ViewClassPermission(
POST=all_permissions.storages_sync,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = ImportStorageSerializer

Expand All @@ -125,7 +134,9 @@ def post(self, request, *args, **kwargs):

class ExportStorageSyncAPI(generics.GenericAPIView):

permission_required = all_permissions.projects_change
permission_required = ViewClassPermission(
POST=all_permissions.storages_sync,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = ExportStorageSerializer

Expand All @@ -147,7 +158,7 @@ def post(self, request, *args, **kwargs):

class StorageValidateAPI(generics.CreateAPIView):

permission_required = all_permissions.projects_change
permission_required = all_permissions.storages_change
parser_classes = (JSONParser, FormParser, MultiPartParser)

def create(self, request, *args, **kwargs):
Expand All @@ -160,8 +171,7 @@ def create(self, request, *args, **kwargs):
@extend_schema(exclude=True)
class ImportStorageListFilesAPI(generics.CreateAPIView):

permission_required = all_permissions.projects_change
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + [StoragePermission]
permission_required = all_permissions.storages_change
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = None # Default serializer

Expand Down Expand Up @@ -203,7 +213,7 @@ def create(self, request, *args, **kwargs):
@extend_schema(exclude=True)
class StorageFormLayoutAPI(generics.RetrieveAPIView):

permission_required = all_permissions.projects_change
permission_required = all_permissions.storages_change
parser_classes = (JSONParser, FormParser, MultiPartParser)
storage_type = None

Expand Down
11 changes: 0 additions & 11 deletions label_studio/io_storages/permissions.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { useHistory, useLocation } from "react-router-dom";
import { useUpdatePageTitle, createTitleFromSegments } from "@humansignal/core";
import { useProject } from "../../../providers/ProjectProvider";
import { cn } from "../../../utils/bem";
import { isInLicense, LF_CLOUD_STORAGE_FOR_MANAGERS } from "../../../utils/license-flags";
import { StorageSet } from "./StorageSet";

const isAllowCloudStorage = !isInLicense(LF_CLOUD_STORAGE_FOR_MANAGERS);

export const StorageSettings = () => {
const { project } = useProject();
const rootClass = cn("storage-settings"); // TODO: Remove in the next BEM cleanup
Expand All @@ -32,7 +29,7 @@ export const StorageSettings = () => {
}
}, [location, history]);

return isAllowCloudStorage ? (
return (
<section className="max-w-[680px]">
<Typography variant="headline" size="medium" className="mb-base">
Cloud Storage
Expand All @@ -57,7 +54,7 @@ export const StorageSettings = () => {
/>
</div>
</section>
) : null;
);
};

StorageSettings.title = "Cloud Storage";
Expand Down
8 changes: 2 additions & 6 deletions web/apps/labelstudio/src/pages/Settings/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ import { LabelingSettings } from "./LabelingSettings";
import { MachineLearningSettings } from "./MachineLearningSettings/MachineLearningSettings";
import { PredictionsSettings } from "./PredictionsSettings/PredictionsSettings";
import { StorageSettings } from "./StorageSettings/StorageSettings";
import { isInLicense, LF_CLOUD_STORAGE_FOR_MANAGERS } from "../../utils/license-flags";
import "./settings.scss";

const isAllowCloudStorage = !isInLicense(LF_CLOUD_STORAGE_FOR_MANAGERS);

export const MenuLayout = ({ children, ...routeProps }) => {
return (
<SidebarMenu
Expand All @@ -21,7 +18,7 @@ export const MenuLayout = ({ children, ...routeProps }) => {
AnnotationSettings,
MachineLearningSettings,
PredictionsSettings,
isAllowCloudStorage && StorageSettings,
StorageSettings,
WebhookPage,
DangerZone,
].filter(Boolean)}
Expand All @@ -36,12 +33,11 @@ const pages = {
LabelingSettings,
MachineLearningSettings,
PredictionsSettings,
StorageSettings,
WebhookPage,
DangerZone,
};

isAllowCloudStorage && (pages.StorageSettings = StorageSettings);

export const SettingsPage = {
title: "Settings",
path: "/settings",
Expand Down
5 changes: 0 additions & 5 deletions web/apps/labelstudio/src/utils/license-flags.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { isFlagEnabled } from "@humansignal/core/lib/utils/helpers";

/**
* Control Visibility and Access of Cloud Storage Connectors for Managers
*/
export const LF_CLOUD_STORAGE_FOR_MANAGERS = "hide_storage_settings_for_manager";

export function isInLicense(id: string) {
return isFlagEnabled(id, window.APP_SETTINGS?.flags || {});
}
Loading