diff --git a/api/controllers/common/wraps.py b/api/controllers/common/wraps.py new file mode 100644 index 0000000000..c481f6eca9 --- /dev/null +++ b/api/controllers/common/wraps.py @@ -0,0 +1,30 @@ +from collections.abc import Callable +from functools import wraps + +from core.rbac import RBACPermission, RBACResourceScope + +__all__ = ["RBACPermission", "RBACResourceScope", "rbac_permission_required"] + + +def rbac_permission_required[**P, R]( + resource_type: RBACResourceScope, + scene: RBACPermission, + *, + resource_required: bool = True, +) -> Callable[[Callable[P, R]], Callable[P, R]]: + """Check enterprise RBAC permissions for the current user. + + Args: + resource_type: The :class:`RBACResourceScope` member (app/dataset/workspace). + scene: The :class:`RBACPermission` permission point. + resource_required: Whether a concrete resource ID is required. + """ + + def decorator(view: Callable[P, R]) -> Callable[P, R]: + @wraps(view) + def decorated(*args: P.args, **kwargs: P.kwargs) -> R: + return view(*args, **kwargs) + + return decorated + + return decorator diff --git a/api/core/rbac/__init__.py b/api/core/rbac/__init__.py new file mode 100644 index 0000000000..495ac90f49 --- /dev/null +++ b/api/core/rbac/__init__.py @@ -0,0 +1,3 @@ +from core.rbac.entities import RBACPermission, RBACResourceScope + +__all__ = ["RBACPermission", "RBACResourceScope"] diff --git a/api/core/rbac/entities.py b/api/core/rbac/entities.py new file mode 100644 index 0000000000..5389e16eef --- /dev/null +++ b/api/core/rbac/entities.py @@ -0,0 +1,37 @@ +from enum import StrEnum + + +class RBACResourceScope(StrEnum): + """Resource scopes accepted by the ``rbac_permission_required`` decorator. + + ``WORKSPACE`` denotes a workspace-level check that carries no concrete + resource id; ``APP`` and ``DATASET`` are resource-scoped checks. + """ + + APP = "app" + DATASET = "dataset" + WORKSPACE = "workspace" + + +class RBACPermission(StrEnum): + """Permission points (RBAC scenes) checked by ``rbac_permission_required``. + + Each member's value is the scene name forwarded to the RBAC + ``check-access`` endpoint. + """ + + APP_VIEW_LAYOUT = "app_view_layout" + APP_TEST_AND_RUN = "app_test_and_run" + APP_CREATE_AND_MANAGEMENT = "app_create_and_management" + APP_RELEASE_AND_VERSION = "app_release_and_version" + APP_IMPORT_EXPORT_DSL = "app_import_export_dsl" + APP_MONITOR = "app_monitor" + APP_DELETE = "app_delete" + + DATASET_READONLY = "dataset_readonly" + DATASET_EDIT = "dataset_edit" + DATASET_CREATE_AND_MANAGEMENT = "dataset_create_and_management" + DATASET_PIPELINE_TEST = "dataset_pipeline_test" + DATASET_DOCUMENT_DOWNLOAD = "dataset_document_download" + + WORKSPACE_ROLE_MANAGE = "workspace_role_manage"