mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
fix(rbac): let agent.manage operate every agent page (#41583)
Co-authored-by: yunlu.wen <yunlu.wen@dify.ai>
This commit is contained in:
1 parent
ea98d38c3d
commit
93dae7d8a0
11 files changed
+152
-80
No files matched your search
@@ -7,7 +7,7 @@ from werkzeug.exceptions import NotFound
|
||||
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
|
||||
from controllers.common.session import with_session
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.app.wraps import agent_manage_required_for_agent_app, get_app_model
|
||||
from controllers.console.wraps import (
|
||||
RBACPermission,
|
||||
RBACResourceScope,
|
||||
@@ -527,8 +527,7 @@ class AgentComposerApi(Resource):
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_EDIT)
|
||||
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.AGENT_MANAGE, resource_required=False)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_EDIT)
|
||||
@with_current_user_id
|
||||
@with_current_tenant_id
|
||||
@with_session
|
||||
|
||||
@@ -32,6 +32,7 @@ from controllers.console.app.app import (
|
||||
from controllers.console.app.app import (
|
||||
UpdateAppPayload as GenericUpdateAppPayload,
|
||||
)
|
||||
from controllers.console.app.wraps import agent_manage_required_for_agent_app
|
||||
from controllers.console.wraps import (
|
||||
RBACPermission,
|
||||
RBACResourceScope,
|
||||
@@ -983,8 +984,7 @@ class AgentApiStatusApi(Resource):
|
||||
@login_required
|
||||
@is_admin_or_owner_required
|
||||
@account_initialization_required
|
||||
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.AGENT_MANAGE, resource_required=False)
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@with_current_tenant_id
|
||||
@with_session
|
||||
@model_validate(AgentApiStatusPayload)
|
||||
@@ -1002,10 +1002,9 @@ class AgentApiKeyListApi(BaseApiKeyListResource):
|
||||
token_prefix = "app-"
|
||||
|
||||
@console_ns.response(200, "Agent service API keys", console_ns.models[ApiKeyList.__name__])
|
||||
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.AGENT_MANAGE, resource_required=False)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@with_current_tenant_id
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@with_session(write=False)
|
||||
def get(self, session: Session, tenant_id: str, agent_id: UUID) -> dict[str, object]:
|
||||
app_model = _resolve_agent_app_model(session, tenant_id=tenant_id, agent_id=agent_id)
|
||||
@@ -1015,8 +1014,7 @@ class AgentApiKeyListApi(BaseApiKeyListResource):
|
||||
@console_ns.response(400, "Maximum keys exceeded")
|
||||
@with_current_tenant_id
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.AGENT_MANAGE, resource_required=False)
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@with_session
|
||||
def post(self, session: Session, tenant_id: str, agent_id: UUID) -> tuple[dict[str, object], int]:
|
||||
app_model = _resolve_agent_app_model(session, tenant_id=tenant_id, agent_id=agent_id)
|
||||
@@ -1035,8 +1033,7 @@ class AgentApiKeyApi(BaseApiKeyResource):
|
||||
@console_ns.response(204, "Agent service API key deleted")
|
||||
@with_current_user
|
||||
@with_current_tenant_id
|
||||
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.AGENT_MANAGE, resource_required=False)
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@with_session
|
||||
def delete(
|
||||
self,
|
||||
|
||||
@@ -23,7 +23,7 @@ from controllers.common.schema import (
|
||||
)
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.error import AppNotFoundError
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.app.wraps import agent_manage_required_for_agent_app, get_app_model
|
||||
from controllers.console.wraps import (
|
||||
RBACPermission,
|
||||
RBACResourceScope,
|
||||
@@ -153,7 +153,7 @@ class AgentAppSandboxInfoResource(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_VIEW_LAYOUT)
|
||||
@with_current_tenant_id
|
||||
@with_current_user
|
||||
def get(self, current_user: Account, tenant_id: str, agent_id: UUID):
|
||||
@@ -183,7 +183,7 @@ class AgentAppSandboxListResource(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_VIEW_LAYOUT)
|
||||
@with_current_tenant_id
|
||||
@with_current_user
|
||||
def get(self, current_user: Account, tenant_id: str, agent_id: UUID):
|
||||
@@ -214,7 +214,7 @@ class AgentAppSandboxReadResource(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_VIEW_LAYOUT)
|
||||
@with_current_tenant_id
|
||||
@with_current_user
|
||||
def get(self, current_user: Account, tenant_id: str, agent_id: UUID):
|
||||
@@ -245,7 +245,7 @@ class AgentAppSandboxDownloadResource(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_VIEW_LAYOUT)
|
||||
@with_current_tenant_id
|
||||
@with_current_user
|
||||
@model_validate(AgentSandboxDownloadPayload)
|
||||
|
||||
@@ -1173,8 +1173,7 @@ class AppSiteStatus(Resource):
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@agent_manage_required_for_agent_app
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_RELEASE_AND_VERSION)
|
||||
@with_session
|
||||
@get_app_model(mode=None)
|
||||
@model_validate(AppSiteStatusPayload)
|
||||
|
||||
@@ -24,7 +24,7 @@ from controllers.console.app.error import (
|
||||
ProviderNotInitializeError,
|
||||
ProviderQuotaExceededError,
|
||||
)
|
||||
from controllers.console.app.wraps import get_app_model, with_session
|
||||
from controllers.console.app.wraps import agent_manage_required_for_agent_app, get_app_model, with_session
|
||||
from controllers.console.wraps import (
|
||||
RBACPermission,
|
||||
RBACResourceScope,
|
||||
@@ -263,7 +263,7 @@ class AgentChatMessageApi(Resource):
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_TEST_AND_RUN)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_TEST_AND_RUN)
|
||||
@with_current_user
|
||||
@with_current_tenant_id
|
||||
@with_session
|
||||
@@ -292,7 +292,7 @@ class AgentBuildChatFinalizeApi(Resource):
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_TEST_AND_RUN)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_TEST_AND_RUN)
|
||||
@with_current_user
|
||||
@with_current_tenant_id
|
||||
@with_session
|
||||
|
||||
@@ -21,7 +21,7 @@ from controllers.console.app.error import (
|
||||
ProviderNotInitializeError,
|
||||
ProviderQuotaExceededError,
|
||||
)
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.app.wraps import agent_manage_required_for_agent_app, get_app_model
|
||||
from controllers.console.explore.error import AppSuggestedQuestionsAfterAnswerDisabledError
|
||||
from controllers.console.wraps import (
|
||||
RBACPermission,
|
||||
@@ -174,7 +174,7 @@ class AgentChatMessageListApi(Resource):
|
||||
@account_initialization_required
|
||||
@setup_required
|
||||
@edit_permission_required
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT)
|
||||
@agent_manage_required_for_agent_app(scene=RBACPermission.APP_VIEW_LAYOUT)
|
||||
@with_current_user
|
||||
@with_current_tenant_id
|
||||
@with_session(write=False)
|
||||
|
||||
@@ -8,7 +8,6 @@ from constants.languages import supported_language
|
||||
from controllers.common.schema import register_schema_models
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.error import AppNotFoundError
|
||||
from controllers.console.app.wraps import agent_manage_required_for_agent_app
|
||||
from controllers.console.flask_admission import console_account_admission
|
||||
from controllers.console.wraps import (
|
||||
RBACPermission,
|
||||
@@ -108,8 +107,8 @@ class AppSite(Resource):
|
||||
allowed_roles=_APP_SITE_EDIT_ROLES,
|
||||
rbac_resource_scope=RBACResourceScope.APP,
|
||||
rbac_permission=RBACPermission.APP_RELEASE_AND_VERSION,
|
||||
agent_manage_fallback=True,
|
||||
)
|
||||
@agent_manage_required_for_agent_app
|
||||
@model_validate(AppSiteUpdatePayload)
|
||||
def post(
|
||||
self,
|
||||
@@ -139,8 +138,8 @@ class AppSiteAccessTokenReset(Resource):
|
||||
allowed_roles=_APP_SITE_TOKEN_RESET_ROLES,
|
||||
rbac_resource_scope=RBACResourceScope.APP,
|
||||
rbac_permission=RBACPermission.APP_RELEASE_AND_VERSION,
|
||||
agent_manage_fallback=True,
|
||||
)
|
||||
@agent_manage_required_for_agent_app
|
||||
def post(self, request_context: RequestContext, app_id: UUID):
|
||||
try:
|
||||
site = application_services().app_sites.reset_access_token(request_context, str(app_id))
|
||||
|
||||
@@ -12,20 +12,22 @@ from typing import cast, overload
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from configs import dify_config
|
||||
from controllers.common.session import with_session
|
||||
from controllers.common.wraps import RBACPermission, RBACResourceScope, enforce_rbac_access
|
||||
from controllers.common.wraps import RBACPermission, RBACResourceScope, _extract_resource_id, enforce_rbac_access
|
||||
from controllers.console.app.error import AppNotFoundError
|
||||
from extensions.ext_application_services import application_services
|
||||
from extensions.ext_database import db
|
||||
from libs.login import current_account_with_tenant
|
||||
from models import App, AppMode
|
||||
from models.agent import AgentScope
|
||||
from models.agent import Agent, AgentScope
|
||||
from services.app_service import AppService
|
||||
|
||||
__all__ = [
|
||||
"agent_manage_required_for_agent_app",
|
||||
"enforce_agent_manage_or_app_scene",
|
||||
"get_app_model",
|
||||
"get_previewable_app_model",
|
||||
"with_session",
|
||||
@@ -57,43 +59,116 @@ def _load_previewable_app_model(session: Session, app_id: str) -> App | None:
|
||||
return AppService.get_normal_app_by_id(app_id, session)
|
||||
|
||||
|
||||
def agent_manage_required_for_agent_app[**P, R](view: Callable[P, R]) -> Callable[P, R]:
|
||||
"""Gate generic app management routes that target an Agent App.
|
||||
def _agent_app_binding(app_id: str) -> Agent | None:
|
||||
app_model = _load_app_model_from_scoped_session(app_id)
|
||||
if app_model is None:
|
||||
return None
|
||||
return app_model.agent_app_binding_with_session(session=db.session(), include_archived=True)
|
||||
|
||||
A hidden workflow-only backing App only reuses the App runtime and is not
|
||||
part of the general app management plane, so generic routes reject it
|
||||
outright. Managing a roster Agent App mutates the roster Agent behind it
|
||||
(rename/icon sync, archive, API enablement), so it additionally requires
|
||||
workspace ``agent.manage`` on top of the route's existing App permission
|
||||
checks when RBAC is enabled. A no-op for non-agent Apps. Must be placed
|
||||
above ``get_app_model`` so the ``app_id`` path parameter is still present.
|
||||
"""
|
||||
|
||||
@wraps(view)
|
||||
def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
raw_app_id = kwargs.get("app_id") or kwargs.get("resource_id")
|
||||
if raw_app_id is not None:
|
||||
app_model = _load_app_model_from_scoped_session(str(raw_app_id))
|
||||
binding = (
|
||||
app_model.agent_app_binding_with_session(session=db.session(), include_archived=True)
|
||||
if app_model is not None
|
||||
else None
|
||||
def _reject_hidden_agent_backing_app(path_args: dict[str, object]) -> None:
|
||||
raw_app_id = path_args.get("app_id") or path_args.get("resource_id")
|
||||
if raw_app_id is None:
|
||||
return
|
||||
binding = _agent_app_binding(str(raw_app_id))
|
||||
if binding is not None and binding.scope == AgentScope.WORKFLOW_ONLY:
|
||||
raise AppNotFoundError()
|
||||
|
||||
|
||||
def enforce_agent_manage_or_app_scene(
|
||||
*,
|
||||
tenant_id: str,
|
||||
account_id: str,
|
||||
scene: RBACPermission,
|
||||
path_args: dict[str, object],
|
||||
) -> None:
|
||||
# Must run before the RBAC_ENABLED check below: a hidden workflow-only
|
||||
# backing App has to stay unreachable regardless of RBAC_ENABLED.
|
||||
_reject_hidden_agent_backing_app(path_args)
|
||||
|
||||
if not dify_config.RBAC_ENABLED:
|
||||
return
|
||||
|
||||
binding = _agent_app_binding(_extract_resource_id(RBACResourceScope.APP, tenant_id, path_args))
|
||||
|
||||
if binding is not None:
|
||||
if binding.scope == AgentScope.WORKFLOW_ONLY:
|
||||
raise AppNotFoundError()
|
||||
try:
|
||||
enforce_rbac_access(
|
||||
tenant_id=tenant_id,
|
||||
account_id=account_id,
|
||||
resource_type=RBACResourceScope.WORKSPACE,
|
||||
scene=RBACPermission.AGENT_MANAGE,
|
||||
resource_required=False,
|
||||
)
|
||||
if binding is not None:
|
||||
if binding.scope == AgentScope.WORKFLOW_ONLY:
|
||||
raise AppNotFoundError()
|
||||
if dify_config.RBAC_ENABLED:
|
||||
current_user, current_tenant_id = current_account_with_tenant()
|
||||
enforce_rbac_access(
|
||||
tenant_id=current_tenant_id,
|
||||
account_id=current_user.id,
|
||||
resource_type=RBACResourceScope.WORKSPACE,
|
||||
scene=RBACPermission.AGENT_MANAGE,
|
||||
resource_required=False,
|
||||
)
|
||||
return view(*args, **kwargs)
|
||||
return
|
||||
except Forbidden:
|
||||
pass # not an agent.manage holder — fall through to the normal scene check
|
||||
|
||||
return decorated
|
||||
enforce_rbac_access(
|
||||
tenant_id=tenant_id,
|
||||
account_id=account_id,
|
||||
resource_type=RBACResourceScope.APP,
|
||||
scene=scene,
|
||||
path_args=path_args,
|
||||
)
|
||||
|
||||
|
||||
@overload
|
||||
def agent_manage_required_for_agent_app[**P, R](view: Callable[P, R]) -> Callable[P, R]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def agent_manage_required_for_agent_app[**P, R](
|
||||
view: None = None, *, scene: RBACPermission | None = None
|
||||
) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
|
||||
|
||||
|
||||
def agent_manage_required_for_agent_app[**P, R](
|
||||
view: Callable[P, R] | None = None, *, scene: RBACPermission | None = None
|
||||
) -> Callable[P, R] | Callable[[Callable[P, R]], Callable[P, R]]:
|
||||
# Must sit above get_app_model in the decorator stack — get_app_model
|
||||
# deletes app_id from kwargs, and this decorator needs it.
|
||||
# TODO: this is a workaround, remove this after ACL for agent app is available
|
||||
def decorator(view_func: Callable[P, R]) -> Callable[P, R]:
|
||||
@wraps(view_func)
|
||||
def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
if scene is not None:
|
||||
if not dify_config.RBAC_ENABLED:
|
||||
_reject_hidden_agent_backing_app(kwargs)
|
||||
return view_func(*args, **kwargs)
|
||||
current_user, current_tenant_id = current_account_with_tenant()
|
||||
enforce_agent_manage_or_app_scene(
|
||||
tenant_id=current_tenant_id,
|
||||
account_id=current_user.id,
|
||||
scene=scene,
|
||||
path_args=kwargs,
|
||||
)
|
||||
return view_func(*args, **kwargs)
|
||||
|
||||
raw_app_id = kwargs.get("app_id") or kwargs.get("resource_id")
|
||||
if raw_app_id is not None:
|
||||
binding = _agent_app_binding(str(raw_app_id))
|
||||
if binding is not None:
|
||||
if binding.scope == AgentScope.WORKFLOW_ONLY:
|
||||
raise AppNotFoundError()
|
||||
if dify_config.RBAC_ENABLED:
|
||||
current_user, current_tenant_id = current_account_with_tenant()
|
||||
enforce_rbac_access(
|
||||
tenant_id=current_tenant_id,
|
||||
account_id=current_user.id,
|
||||
resource_type=RBACResourceScope.WORKSPACE,
|
||||
scene=RBACPermission.AGENT_MANAGE,
|
||||
resource_required=False,
|
||||
)
|
||||
return view_func(*args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
||||
if view is None:
|
||||
return decorator
|
||||
return decorator(view)
|
||||
|
||||
|
||||
def _get_injected_session(args: tuple[object, ...]) -> Session | None:
|
||||
|
||||
@@ -9,6 +9,7 @@ from werkzeug.exceptions import Forbidden
|
||||
|
||||
from configs import dify_config
|
||||
from controllers.common.wraps import enforce_rbac_access
|
||||
from controllers.console.app.wraps import enforce_agent_manage_or_app_scene
|
||||
from controllers.console.wraps import (
|
||||
account_initialization_required,
|
||||
enable_change_email,
|
||||
@@ -52,6 +53,7 @@ def console_account_admission[T, **P, R](
|
||||
rbac_resource_scope: RBACResourceScope | None = None,
|
||||
rbac_permission: RBACPermission | None = None,
|
||||
rbac_resource_required: bool = True,
|
||||
agent_manage_fallback: bool = False,
|
||||
) -> Callable[
|
||||
[Callable[Concatenate[T, RequestContext, P], R]],
|
||||
Callable[Concatenate[T, P], R | Response],
|
||||
@@ -66,6 +68,10 @@ def console_account_admission[T, **P, R](
|
||||
|
||||
if (rbac_resource_scope is None) != (rbac_permission is None):
|
||||
raise AdmissionConfigurationError("RBAC resource scope and permission must be configured together")
|
||||
if agent_manage_fallback and rbac_resource_scope != RBACResourceScope.APP:
|
||||
raise AdmissionConfigurationError("agent_manage_fallback requires rbac_resource_scope=RBACResourceScope.APP")
|
||||
if agent_manage_fallback and not rbac_resource_required:
|
||||
raise AdmissionConfigurationError("agent_manage_fallback requires rbac_resource_required=True")
|
||||
|
||||
def decorator(
|
||||
view: Callable[Concatenate[T, RequestContext, P], R],
|
||||
@@ -78,14 +84,22 @@ def console_account_admission[T, **P, R](
|
||||
if allowed_roles is not None and not dify_config.RBAC_ENABLED and account.role not in allowed_roles:
|
||||
raise Forbidden()
|
||||
if rbac_resource_scope is not None and rbac_permission is not None:
|
||||
enforce_rbac_access(
|
||||
tenant_id=tenant_id,
|
||||
account_id=account.id,
|
||||
resource_type=rbac_resource_scope,
|
||||
scene=rbac_permission,
|
||||
resource_required=rbac_resource_required,
|
||||
path_args=kwargs,
|
||||
)
|
||||
if agent_manage_fallback:
|
||||
enforce_agent_manage_or_app_scene(
|
||||
tenant_id=tenant_id,
|
||||
account_id=account.id,
|
||||
scene=rbac_permission,
|
||||
path_args=kwargs,
|
||||
)
|
||||
else:
|
||||
enforce_rbac_access(
|
||||
tenant_id=tenant_id,
|
||||
account_id=account.id,
|
||||
resource_type=rbac_resource_scope,
|
||||
scene=rbac_permission,
|
||||
resource_required=rbac_resource_required,
|
||||
path_args=kwargs,
|
||||
)
|
||||
request_context = RequestContext(
|
||||
account_id=account.id,
|
||||
active_workspace_id=tenant_id,
|
||||
|
||||
@@ -149,10 +149,6 @@ def _account() -> Account:
|
||||
@pytest.mark.parametrize(
|
||||
"method",
|
||||
[
|
||||
module.AgentAppSandboxInfoResource.get,
|
||||
module.AgentAppSandboxListResource.get,
|
||||
module.AgentAppSandboxReadResource.get,
|
||||
module.AgentAppSandboxDownloadResource.post,
|
||||
module.WorkflowAgentSandboxListResource.get,
|
||||
module.WorkflowAgentSandboxReadResource.get,
|
||||
module.WorkflowAgentSandboxDownloadResource.post,
|
||||
|
||||
@@ -262,13 +262,6 @@ def test_api_key_lists_require_matching_rbac_permission(config_overrides: Callab
|
||||
lambda: AppApiKeyListResource().get(resource_id=api_id),
|
||||
[(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION, True)],
|
||||
),
|
||||
(
|
||||
lambda: AgentApiKeyListApi().get(agent_id=api_id),
|
||||
[
|
||||
(RBACResourceScope.WORKSPACE, RBACPermission.AGENT_MANAGE, False),
|
||||
(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION, True),
|
||||
],
|
||||
),
|
||||
(
|
||||
lambda: DatasetApiKeyApi().get(),
|
||||
[(RBACResourceScope.DATASET, RBACPermission.DATASET_API_KEY_MANAGE, False)],
|
||||
|
||||
Reference in new issue
Block a user