diff --git a/apps/application/views/application_stats.py b/apps/application/views/application_stats.py index dae396a6b98..25fc4ebb547 100644 --- a/apps/application/views/application_stats.py +++ b/apps/application/views/application_stats.py @@ -284,8 +284,8 @@ class TokenUsageExport(APIView): tags=[_("Application")], # type: ignore ) @has_permissions( - PermissionConstants.APPLICATION_EXPORT.get_workspace_application_permission(), - PermissionConstants.APPLICATION_EXPORT.get_workspace_permission_workspace_manage_role(), + PermissionConstants.APPLICATION_OVERVIEW_EXPORT.get_workspace_application_permission(), + PermissionConstants.APPLICATION_OVERVIEW_EXPORT.get_workspace_permission_workspace_manage_role(), ViewPermission( [RoleConstants.USER.get_workspace_role()], [PermissionConstants.APPLICATION.get_workspace_application_permission()], @@ -318,8 +318,8 @@ class TopQuestionsExport(APIView): tags=[_("Application")], # type: ignore ) @has_permissions( - PermissionConstants.APPLICATION_EXPORT.get_workspace_application_permission(), - PermissionConstants.APPLICATION_EXPORT.get_workspace_permission_workspace_manage_role(), + PermissionConstants.APPLICATION_OVERVIEW_EXPORT.get_workspace_application_permission(), + PermissionConstants.APPLICATION_OVERVIEW_EXPORT.get_workspace_permission_workspace_manage_role(), ViewPermission( [RoleConstants.USER.get_workspace_role()], [PermissionConstants.APPLICATION.get_workspace_application_permission()], diff --git a/apps/common/auth/constants/permission_constants.py b/apps/common/auth/constants/permission_constants.py index a249b9c3977..95ca045e7d9 100644 --- a/apps/common/auth/constants/permission_constants.py +++ b/apps/common/auth/constants/permission_constants.py @@ -1882,6 +1882,16 @@ class PermissionConstants(Enum): ), ) + APPLICATION_OVERVIEW_EXPORT = ( + Permission(group=Group.APPLICATION, sub_group=Group.OVERVIEW, operate=Operate.EXPORT, bit_index=43), + PermissionMeta( + role_list=[RoleConstants.WORKSPACE_MANAGE, RoleConstants.USER], + category=Category.WORKSPACE, + scope=[PermissionScopeConstants.WORKSPACE_RESOURCE, PermissionScopeConstants.WORKSPACE], + resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANAGE], + ), + ) + APPLICATION_OVERVIEW_BACKEND_API_ACCESS = ( Permission( group=Group.APPLICATION, @@ -3726,6 +3736,21 @@ class PermissionConstants(Enum): ), ) + RESOURCE_APPLICATION_OVERVIEW_EXPORT = ( + Permission( + group=Group.SYSTEM_RES_APPLICATION, + sub_group=Group.SYSTEM_OVERVIEW, + operate=Operate.EXPORT, + bit_index=32, + ), + PermissionMeta( + role_list=[RoleConstants.ADMIN], + category=Category.RESOURCE, + is_ee=is_ee, + scope=[PermissionScopeConstants.SYSTEM], + ), + ) + RESOURCE_APPLICATION_OVERVIEW_BACKEND_API_ACCESS = ( Permission( group=Group.SYSTEM_RES_APPLICATION, diff --git a/apps/models_provider/tests/test_model_workspace_uniqueness.py b/apps/models_provider/tests/test_model_workspace_uniqueness.py new file mode 100644 index 00000000000..006862e2204 --- /dev/null +++ b/apps/models_provider/tests/test_model_workspace_uniqueness.py @@ -0,0 +1,71 @@ +from unittest.mock import MagicMock, patch + +from django.test import TestCase + +from common.exception.app_exception import AppApiException +from models_provider.models import Model, Status +from models_provider.serializers.model_serializer import ModelSerializer +from users.models import User + +MODEL_NAME = "zws-LLM-dp001" + + +class ModelWorkspaceUniquenessTests(TestCase): + def setUp(self): + self.user = User.objects.create( + username="model-workspace-uniqueness-user", + nick_name="Model workspace uniqueness user", + password="password", + role="ADMIN", + ) + self.provider = MagicMock() + self.provider.get_model_type_list.return_value = [{"value": "LLM"}] + + def create_model(self, workspace_id): + return Model.objects.create( + name=MODEL_NAME, + status=Status.SUCCESS, + model_type="LLM", + model_name="deepseek-chat", + user=self.user, + provider="test_provider", + credential="encrypted-credential", + model_params_form=[], + workspace_id=workspace_id, + ) + + def serializer_data(self, workspace_id): + return { + "user_id": str(self.user.id), + "name": MODEL_NAME, + "provider": "test_provider", + "model_type": "LLM", + "model_name": "deepseek-chat", + "model_params_form": [], + "credential": {"api_key": "secret"}, + "workspace_id": workspace_id, + } + + def test_create_same_name_in_other_workspace_uses_insert_workspace(self): + self.create_model("workspace-a") + + with ( + patch( + "models_provider.serializers.model_serializer.ModelProvideConstants", {"test_provider": self.provider} + ), + patch("models_provider.serializers.model_serializer.UserResourcePermissionSerializer"), + ): + result = ModelSerializer.Create(data=self.serializer_data("workspace-a")).insert("workspace-b") + + self.assertEqual("workspace-b", Model.objects.get(id=result["id"]).workspace_id) + + def test_create_same_name_in_same_workspace_is_rejected(self): + self.create_model("workspace-a") + + with ( + patch( + "models_provider.serializers.model_serializer.ModelProvideConstants", {"test_provider": self.provider} + ), + self.assertRaises(AppApiException), + ): + ModelSerializer.Create(data=self.serializer_data("workspace-a")).insert("workspace-a") diff --git a/ui/src/permission/core/data.ts b/ui/src/permission/core/data.ts index c2f570c7855..565baecc0fe 100644 --- a/ui/src/permission/core/data.ts +++ b/ui/src/permission/core/data.ts @@ -228,6 +228,7 @@ export const PermissionConstants = { APPLICATION_OVERVIEW_DISPLAY: p(Group.APPLICATION, Group.OVERVIEW, Operate.DISPLAY, 26), APPLICATION_OVERVIEW_API_KEY: p(Group.APPLICATION, Group.OVERVIEW, Operate.API_KEY, 27), APPLICATION_OVERVIEW_PUBLIC: p(Group.APPLICATION, Group.OVERVIEW, Operate.PUBLIC_ACCESS, 28), + APPLICATION_OVERVIEW_EXPORT: p(Group.APPLICATION, Group.OVERVIEW, Operate.EXPORT, 43), APPLICATION_OVERVIEW_BACKEND_API_ACCESS: p(Group.APPLICATION, Group.OVERVIEW, Operate.BACKEND_API_ACCESS, 38), APPLICATION_ACCESS_READ: p(Group.APPLICATION, Group.ACCESS, Operate.READ, 29), APPLICATION_ACCESS_EDIT: p(Group.APPLICATION, Group.ACCESS, Operate.EDIT, 30), @@ -387,6 +388,7 @@ export const PermissionConstants = { RESOURCE_APPLICATION_OVERVIEW_DISPLAY: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.DISPLAY, 17), RESOURCE_APPLICATION_OVERVIEW_API_KEY: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.API_KEY, 18), RESOURCE_APPLICATION_OVERVIEW_PUBLIC: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.PUBLIC_ACCESS, 19), + RESOURCE_APPLICATION_OVERVIEW_EXPORT: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.EXPORT, 32), RESOURCE_APPLICATION_OVERVIEW_BACKEND_API_ACCESS: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.BACKEND_API_ACCESS, 29), RESOURCE_APPLICATION_ACCESS_READ: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_ACCESS, Operate.READ, 20), RESOURCE_APPLICATION_ACCESS_EDIT: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_ACCESS, Operate.EDIT, 21), diff --git a/ui/src/permission/modules/application/system.ts b/ui/src/permission/modules/application/system.ts index 16b0a86781b..5cc76126f97 100644 --- a/ui/src/permission/modules/application/system.ts +++ b/ui/src/permission/modules/application/system.ts @@ -90,6 +90,10 @@ const system = { * 系统「智能体」「概览」只读权限 */ overviewRead: () => canSys(P.RESOURCE_APPLICATION_OVERVIEW_READ), +/** + * 系统「智能体」「概览」导出权限 + */ + overviewExport: () => canSys(P.RESOURCE_APPLICATION_OVERVIEW_EXPORT), /** * 系统「智能体」「概览」嵌入第三方权限 */ diff --git a/ui/src/permission/modules/application/workspace.ts b/ui/src/permission/modules/application/workspace.ts index c95933d83b5..9e06498c15b 100644 --- a/ui/src/permission/modules/application/workspace.ts +++ b/ui/src/permission/modules/application/workspace.ts @@ -125,6 +125,10 @@ const workspace = { * 工作空间「智能体」「概览」只读权限 */ overviewRead: (id: string) => canRes(P.APPLICATION_OVERVIEW_READ, id), +/** + * 工作空间「智能体」「概览」导出权限 + */ + overviewExport: (id: string) => canRes(P.APPLICATION_OVERVIEW_EXPORT, id), /** * 工作空间「智能体」「概览」嵌入第三方权限 */ diff --git a/ui/src/views/application-detail/overview/components/ApplicationRankingDrawer.vue b/ui/src/views/application-detail/overview/components/ApplicationRankingDrawer.vue index a357b233e9b..c2e7cae80ce 100644 --- a/ui/src/views/application-detail/overview/components/ApplicationRankingDrawer.vue +++ b/ui/src/views/application-detail/overview/components/ApplicationRankingDrawer.vue @@ -1,6 +1,8 @@