diff --git a/api/controllers/console/auth/data_source_bearer_auth.py b/api/controllers/console/auth/data_source_bearer_auth.py index a9c97401105..1de206c73db 100644 --- a/api/controllers/console/auth/data_source_bearer_auth.py +++ b/api/controllers/console/auth/data_source_bearer_auth.py @@ -83,7 +83,7 @@ class ApiKeyAuthDataSourceBinding(Resource): @login_required @account_initialization_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_CREATE, resource_required=False) @console_ns.expect(console_ns.models[ApiKeyAuthBindingPayload.__name__]) @with_current_tenant_id def post(self, current_tenant_id: str): diff --git a/api/controllers/console/datasets/rag_pipeline/datasource_auth.py b/api/controllers/console/datasets/rag_pipeline/datasource_auth.py index c5ca1d155de..a575760ee19 100644 --- a/api/controllers/console/datasets/rag_pipeline/datasource_auth.py +++ b/api/controllers/console/datasets/rag_pipeline/datasource_auth.py @@ -222,7 +222,7 @@ class DatasourceAuth(Resource): @login_required @account_initialization_required @edit_permission_required - @rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.CREDENTIAL_CREATE, resource_required=False) @with_current_tenant_id def post(self, current_tenant_id: str, provider_id: str): payload = DatasourceCredentialPayload.model_validate(console_ns.payload or {}) diff --git a/api/controllers/console/workspace/__init__.py b/api/controllers/console/workspace/__init__.py index 59dd29fdace..13bc98c8047 100644 --- a/api/controllers/console/workspace/__init__.py +++ b/api/controllers/console/workspace/__init__.py @@ -5,6 +5,7 @@ from sqlalchemy import select from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import Forbidden +from configs import dify_config from extensions.ext_database import db from libs.login import current_account_with_tenant from models.account import TenantPluginPermission @@ -17,6 +18,9 @@ def plugin_permission_required( def interceptor[**P, R](view: Callable[P, R]) -> Callable[P, R]: @wraps(view) def decorated(*args: P.args, **kwargs: P.kwargs) -> R: + if dify_config.RBAC_ENABLED: + return view(*args, **kwargs) + current_user, current_tenant_id = current_account_with_tenant() user = current_user tenant_id = current_tenant_id diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py index 8fda67f4ef8..3ce7211703e 100644 --- a/api/controllers/console/workspace/model_providers.py +++ b/api/controllers/console/workspace/model_providers.py @@ -169,7 +169,7 @@ class ModelProviderCredentialApi(Resource): @setup_required @login_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_CREATE, resource_required=False) @account_initialization_required @with_current_tenant_id def post(self, current_tenant_id: str, provider: str): @@ -244,7 +244,7 @@ class ModelProviderCredentialSwitchApi(Resource): @setup_required @login_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False) @account_initialization_required @with_current_tenant_id def post(self, current_tenant_id: str, provider: str): @@ -326,7 +326,7 @@ class PreferredProviderTypeUpdateApi(Resource): @setup_required @login_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False) @account_initialization_required @with_current_tenant_id def post(self, tenant_id: str, provider: str): diff --git a/api/controllers/console/workspace/models.py b/api/controllers/console/workspace/models.py index e82c0fbc2db..1da72ef4362 100644 --- a/api/controllers/console/workspace/models.py +++ b/api/controllers/console/workspace/models.py @@ -395,7 +395,7 @@ class ModelProviderModelCredentialApi(Resource): @setup_required @login_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_CREATE, resource_required=False) @account_initialization_required @with_current_tenant_id def post(self, tenant_id: str, provider: str): @@ -481,7 +481,7 @@ class ModelProviderModelCredentialSwitchApi(Resource): @setup_required @login_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False) @account_initialization_required @with_current_tenant_id def post(self, current_tenant_id: str, provider: str): diff --git a/api/controllers/console/workspace/plugin.py b/api/controllers/console/workspace/plugin.py index d599466002d..e768bb5acde 100644 --- a/api/controllers/console/workspace/plugin.py +++ b/api/controllers/console/workspace/plugin.py @@ -469,6 +469,7 @@ class PluginDebuggingKeyApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_DEBUG, resource_required=False) @plugin_permission_required(debug_required=True) @with_current_tenant_id def get(self, tenant_id: str): @@ -614,6 +615,7 @@ class PluginUploadFromPkgApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -634,6 +636,7 @@ class PluginUploadFromGithubApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -653,6 +656,7 @@ class PluginUploadFromBundleApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -673,6 +677,7 @@ class PluginInstallFromPkgApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -693,6 +698,7 @@ class PluginInstallFromGithubApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -719,6 +725,7 @@ class PluginInstallFromMarketplaceApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -739,6 +746,7 @@ class PluginFetchMarketplacePkgApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def get(self, tenant_id: str): @@ -764,6 +772,7 @@ class PluginFetchManifestApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def get(self, tenant_id: str): @@ -784,6 +793,7 @@ class PluginFetchInstallTasksApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def get(self, tenant_id: str): @@ -801,6 +811,7 @@ class PluginFetchInstallTaskApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def get(self, tenant_id: str, task_id: str): @@ -816,6 +827,7 @@ class PluginDeleteInstallTaskApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str, task_id: str): @@ -831,6 +843,7 @@ class PluginDeleteAllInstallTaskItemsApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -846,6 +859,7 @@ class PluginDeleteInstallTaskItemApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str, task_id: str, identifier: str): @@ -862,6 +876,7 @@ class PluginUpgradeFromMarketplaceApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -884,6 +899,7 @@ class PluginUpgradeFromGithubApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -911,6 +927,7 @@ class PluginUninstallApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False) @plugin_permission_required(install_required=True) @with_current_tenant_id def post(self, tenant_id: str): @@ -1041,10 +1058,11 @@ class PluginChangeAutoUpgradeApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_PREFERENCES, resource_required=False) @with_current_user @with_current_tenant_id def post(self, tenant_id: str, user: Account): - if not user.is_admin_or_owner: + if not dify_config.RBAC_ENABLED and not user.is_admin_or_owner: raise Forbidden() args = ParserAutoUpgradeChange.model_validate(console_ns.payload) @@ -1097,6 +1115,7 @@ class PluginAutoUpgradeExcludePluginApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_PREFERENCES, resource_required=False) @with_current_tenant_id def post(self, tenant_id: str): # exclude one single plugin diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index 9a92571594c..4125e7d8de8 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -971,7 +971,7 @@ class ToolBuiltinProviderSetDefaultApi(Resource): @setup_required @login_required @is_admin_or_owner_required - @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False) @account_initialization_required @with_current_tenant_id def post(self, current_tenant_id: str, provider: str): @@ -1070,6 +1070,7 @@ class ToolProviderMCPApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False) @with_current_user @with_current_tenant_id def post(self, tenant_id: str, user: Account): @@ -1125,6 +1126,7 @@ class ToolProviderMCPApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False) @with_current_tenant_id def put(self, current_tenant_id: str): payload = MCPProviderUpdatePayload.model_validate(console_ns.payload or {}) @@ -1178,6 +1180,7 @@ class ToolProviderMCPApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False) @with_current_tenant_id def delete(self, current_tenant_id: str): payload = MCPProviderDeletePayload.model_validate(console_ns.payload or {}) @@ -1196,6 +1199,7 @@ class ToolMCPAuthApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False) @with_current_tenant_id def post(self, tenant_id: str): payload = MCPAuthPayload.model_validate(console_ns.payload or {}) @@ -1300,6 +1304,7 @@ class ToolMCPUpdateApi(Resource): @setup_required @login_required @account_initialization_required + @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False) @with_current_tenant_id def get(self, tenant_id: str, provider_id: str): with sessionmaker(db.engine).begin() as session: diff --git a/api/core/rbac/entities.py b/api/core/rbac/entities.py index 7f08a530f57..d65f11edf7b 100644 --- a/api/core/rbac/entities.py +++ b/api/core/rbac/entities.py @@ -22,23 +22,35 @@ class RBACPermission(StrEnum): APP_VIEW_LAYOUT = "app_view_layout" APP_TEST_AND_RUN = "app_test_and_run" + APP_PREVIEW = "app_preview" 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_EDIT = "app_edit" APP_MONITOR = "app_monitor" APP_DELETE = "app_delete" + APP_ACCESS_CONFIG = "app_access_config" + DATASET_PREVIEW = "dataset_preview" 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" + DATASET_RETRIEVAL_RECALL = "dataset_retrieval_recall" + DATASET_USE = "dataset_use" + DATASET_DELETE_FILE = "dataset_delete_file" + DATASET_PIPELINE_RELEASE = "dataset_pipeline_release" + DATASET_DELETE = "dataset_delete" + DATASET_ACCESS_CONFIG = "dataset_access_config" DATASET_API_KEY_MANAGE = "dataset_api_key_manage" DATASET_EXTERNAL_CONNECT = "dataset_external_connect" DATASET_IMPORT_EXPORT_DSL = "dataset_import_export_dsl" + WORKSPACE_MEMBER_MANAGE = "workspace_member_manage" WORKSPACE_ROLE_MANAGE = "workspace_role_manage" + API_EXTENSION_MANAGE = "api_extension_manage" + CUSTOMIZATION_MANAGE = "customization_manage" SNIPPETS_CREATE_AND_MODIFY = "snippets_create_and_modify" SNIPPETS_MANAGE = "snippets_management" @@ -49,6 +61,7 @@ class RBACPermission(StrEnum): PLUGIN_DEBUG = "plugin_debug" CREDENTIAL_USE = "credential_use" + CREDENTIAL_CREATE = "credential_create" CREDENTIAL_MANAGE = "credential_manage" TOOL_MANAGE = "tool_manage" diff --git a/api/services/enterprise/base.py b/api/services/enterprise/base.py index 71deec752e5..7ded11a1658 100644 --- a/api/services/enterprise/base.py +++ b/api/services/enterprise/base.py @@ -182,6 +182,10 @@ class EnterpriseRequest(BaseRequest): inner_headers: dict[str, str] = {INNER_TENANT_ID_HEADER: tenant_id} if account_id: inner_headers[INNER_ACCOUNT_ID_HEADER] = account_id + + if not cls.base_url.startswith("http") or not cls.base_url.startswith("https") or not cls.base_url: + raise ValueError("ENTERPRISE_RBAC_API_URL is required when RBAC_ENABLED=true") + url = f"{cls.rbac_base_url}{endpoint}" mounts = cls._build_mounts() diff --git a/api/services/enterprise/rbac_service.py b/api/services/enterprise/rbac_service.py index 32cf69ce99e..dd1a50157f7 100644 --- a/api/services/enterprise/rbac_service.py +++ b/api/services/enterprise/rbac_service.py @@ -312,6 +312,7 @@ _LEGACY_WORKSPACE_OWNER_KEYS: list[str] = [ "plugin.manage", "plugin.debug", "credential.use", + "credential.create", "credential.manage", "billing.view", "billing.subscription.manage", @@ -344,6 +345,7 @@ _LEGACY_WORKSPACE_ADMIN_KEYS: list[str] = [ "plugin.manage", "plugin.debug", "credential.use", + "credential.create", "credential.manage", "billing.view", "billing.subscription.manage",