diff --git a/apps/system_manage/models/__init__.py b/apps/system_manage/models/__init__.py index de01c428b10..9620d8d8316 100644 --- a/apps/system_manage/models/__init__.py +++ b/apps/system_manage/models/__init__.py @@ -7,6 +7,7 @@ @desc: """ from .workspace_user_permission import * +from .workspace_user_group_permission import * from .system_setting import * from .log_management import * from .chat_user import * \ No newline at end of file diff --git a/apps/system_manage/models/workspace_user_group_permission.py b/apps/system_manage/models/workspace_user_group_permission.py new file mode 100644 index 00000000000..24421f3e5d0 --- /dev/null +++ b/apps/system_manage/models/workspace_user_group_permission.py @@ -0,0 +1,53 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: workspace_permission.py + @date:2025/4/16 18:25 + @desc: +""" + +import uuid_utils.compat as uuid +from django.contrib.postgres.fields import ArrayField +from django.db import models + +from common.constants.permission_constants import ResourceAuthType, ResourcePermissionRole, ResourcePermission +from users.models.user_group import SystemUserGroup + +from .workspace_user_permission import AuthTargetType + + +class WorkspaceUserGroupResourcePermission(models.Model): + """ + 工作空间用户组资源权限表 + 用于管理当前工作空间下用户组对某一个应用或者知识库的操作权限 + """ + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") + + workspace_id = models.CharField(max_length=128, verbose_name="工作空间id", default="default", db_index=True) + + user_group = models.ForeignKey(SystemUserGroup, on_delete=models.CASCADE, verbose_name="用户组id", db_index=True) + + auth_target_type = models.CharField(verbose_name='授权目标', max_length=128, choices=AuthTargetType.choices, + default=AuthTargetType.KNOWLEDGE, db_index=True) + # 授权的知识库或者应用的id + target = models.CharField(max_length=128, verbose_name="知识库/应用id", db_index=True) + + # 授权类型 如果是Role那么就是角色的权限 如果是PERMISSION + auth_type = models.CharField(default=False, verbose_name="授权类型", choices=ResourceAuthType.choices, + db_default=ResourceAuthType.ROLE, db_index=True) + # 资源权限列表 + permission_list = ArrayField(verbose_name="权限列表", + default=list, + base_field=models.CharField(max_length=256, + blank=True, + choices=ResourcePermission.choices + ResourcePermissionRole.choices, + default=ResourcePermission.VIEW)) + + create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, db_index=True) + + update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True, db_index=True) + + class Meta: + db_table = "workspace_user_group_resource_permission" + unique_together = ('workspace_id', 'user_group', 'auth_target_type', 'target') diff --git a/apps/system_manage/models/workspace_user_permission.py b/apps/system_manage/models/workspace_user_permission.py index d20bfa6ed97..eb52fddc4a2 100644 --- a/apps/system_manage/models/workspace_user_permission.py +++ b/apps/system_manage/models/workspace_user_permission.py @@ -11,8 +11,7 @@ from django.contrib.postgres.fields import ArrayField from django.db import models -from common.constants.permission_constants import Group, ResourcePermissionGroup, ResourceAuthType, \ - ResourcePermissionRole, ResourcePermission +from common.constants.permission_constants import Group, ResourceAuthType, ResourcePermissionRole, ResourcePermission from users.models import User diff --git a/apps/system_manage/serializers/user_group_resource_permission.py b/apps/system_manage/serializers/user_group_resource_permission.py new file mode 100644 index 00000000000..2a13a01c8af --- /dev/null +++ b/apps/system_manage/serializers/user_group_resource_permission.py @@ -0,0 +1,439 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: workspace_user_resource_permission.py + @date:2025/4/28 17:17 + @desc: +""" +import json +import os + +from django.core.cache import cache +from django.db import models +from django.db.models import QuerySet, Q, TextField +from django.db.models.functions import Cast +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from application.models import Application +from common.constants.cache_version import Cache_Version +from common.constants.permission_constants import RoleConstants, \ + ResourcePermission, ResourcePermissionRole, ResourceAuthType +from common.database_model_manage.database_model_manage import DatabaseModelManage +from common.db.search import native_search, native_page_search, get_dynamics_model +from common.db.sql_execute import select_list +from common.exception.app_exception import AppApiException +from common.utils.common import get_file_content +from knowledge.models import Knowledge +from maxkb.conf import PROJECT_DIR +from maxkb.settings import edition +from models_provider.models import Model +from system_manage.models import WorkspaceUserResourcePermission, WorkspaceUserGroupResourcePermission +from tools.models import Tool + + +class PermissionSerializer(serializers.Serializer): + VIEW = serializers.BooleanField(required=True, label="可读") + MANAGE = serializers.BooleanField(required=True, label="管理") + ROLE = serializers.BooleanField(required=True, label="跟随角色") + + +class UserResourcePermissionItemResponse(serializers.Serializer): + id = serializers.UUIDField(required=True, label="主键id") + name = serializers.CharField(required=True, label="资源名称") + auth_target_type = serializers.CharField(required=True, label="授权资源") + user_id = serializers.UUIDField(required=True, label="用户id") + icon = serializers.CharField(required=True, label="资源图标") + auth_type = serializers.CharField(required=True, label="授权类型") + permission = serializers.ChoiceField(required=False, allow_null=True, allow_blank=True, + choices=['NOT_AUTH', 'MANAGE', 'VIEW', 'ROLE'], + label=_('permission')) + + +class UserResourcePermissionResponse(serializers.Serializer): + KNOWLEDGE = UserResourcePermissionItemResponse(many=True) + + +class UpdateTeamMemberItemPermissionSerializer(serializers.Serializer): + target_id = serializers.CharField(required=True, label=_('target id')) + permission = serializers.ChoiceField(required=False, allow_null=True, allow_blank=True, + choices=['NOT_AUTH', 'MANAGE', 'VIEW', 'ROLE'], + label=_('permission')) + + +class UpdateUserResourcePermissionRequest(serializers.Serializer): + user_resource_permission_list = UpdateTeamMemberItemPermissionSerializer(required=True, many=True) + + def is_valid(self, *, auth_target_type=None, workspace_id=None, raise_exception=False): + super().is_valid(raise_exception=True) + user_resource_permission_list = [{'target_id': urp.get('target_id'), 'auth_target_type': auth_target_type} for + urp in + self.data.get("user_resource_permission_list")] + illegal_target_id_list = select_list( + get_file_content( + os.path.join(PROJECT_DIR, "apps", "system_manage", 'sql', 'check_member_permission_target_exists.sql')), + [json.dumps(user_resource_permission_list), workspace_id, workspace_id, workspace_id, workspace_id, + workspace_id, workspace_id, workspace_id]) + if illegal_target_id_list is not None and len(illegal_target_id_list) > 0: + raise AppApiException(500, + _('Non-existent id') + '[' + str(illegal_target_id_list) + ']') + + +m_map = { + "KNOWLEDGE": Knowledge, + 'TOOL': Tool, + 'MODEL': Model, + 'APPLICATION': Application, +} + +sql_map = { + "KNOWLEDGE": 'get_knowledge_user_group_resource_permission.sql', + 'TOOL': 'get_tool_user_group_resource_permission.sql', + 'MODEL': 'get_model_user_group_resource_permission.sql', + 'APPLICATION': 'get_application_user_group_resource_permission.sql' +} + + +class UserResourcePermissionUserListRequest(serializers.Serializer): + name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('resource name')) + permission = serializers.MultipleChoiceField(required=False, allow_null=True, allow_blank=True, + choices=['NOT_AUTH', 'MANAGE', 'VIEW', 'ROLE'], + label=_('permission')) + + +class UserGroupResourcePermissionSerializer(serializers.Serializer): + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + user_group_id = serializers.CharField(required=True, label=_('User Group id')) + auth_target_type = serializers.CharField(required=True, label=_('resource')) + + def get_queryset(self, instance): + resource_query_set = QuerySet( + model=get_dynamics_model({ + 'name': models.CharField(), + "permission": models.CharField(), + })) + name = instance.get('name') + permission = instance.get('permission') + query_p_list = [None if p == "NOT_AUTH" else p for p in permission] + + if name: + resource_query_set = resource_query_set.filter(name__contains=name) + if permission: + if all([p is None for p in query_p_list]): + resource_query_set = resource_query_set.filter(permission=None) + else: + if any([p is None for p in query_p_list]): + resource_query_set = resource_query_set.filter( + Q(permission__in=query_p_list) | Q(permission=None)) + else: + resource_query_set = resource_query_set.filter( + permission__in=query_p_list) + return { + 'query_set': QuerySet(m_map.get(self.data.get('auth_target_type'))).filter( + workspace_id=self.data.get('workspace_id')), + 'folder_query_set': QuerySet(m_map.get(self.data.get('auth_target_type'))).filter( + workspace_id=self.data.get('workspace_id')), + 'workspace_user_group_resource_permission_query_set': QuerySet(WorkspaceUserGroupResourcePermission).filter( + workspace_id=self.data.get('workspace_id'), user_group_id=self.data.get('user_group_id'), + auth_target_type=self.data.get('auth_target_type')), + 'resource_query_set': resource_query_set + } + + def auth_resource_batch(self, resource_id_list: list): + self.is_valid(raise_exception=True) + auth_target_type = self.data.get('auth_target_type') + workspace_id = self.data.get('workspace_id') + user_id = self.data.get('user_id') + wurp = QuerySet(WorkspaceUserResourcePermission).filter(auth_target_type=auth_target_type, + workspace_id=workspace_id, user_id=user_id).first() + auth_type = wurp.auth_type if wurp else ( + ResourceAuthType.RESOURCE_PERMISSION_GROUP if edition == 'CE' else ResourceAuthType.ROLE) + workspace_user_resource_permission = [WorkspaceUserResourcePermission( + target=resource_id, + auth_target_type=auth_target_type, + permission_list=[ResourcePermission.VIEW, + ResourcePermission.MANAGE] if auth_type == ResourceAuthType.RESOURCE_PERMISSION_GROUP else [ + ResourcePermissionRole.ROLE], + workspace_id=workspace_id, + user_id=user_id, + auth_type=auth_type + ) for resource_id in resource_id_list] + QuerySet(WorkspaceUserResourcePermission).bulk_create(workspace_user_resource_permission) + # 刷新缓存 + version = Cache_Version.PERMISSION_LIST.get_version() + key = Cache_Version.PERMISSION_LIST.get_key(user_id=user_id) + cache.delete(key, version=version) + return True + + def auth_resource(self, resource_id: str, is_folder=False): + self.is_valid(raise_exception=True) + auth_target_type = self.data.get('auth_target_type') + workspace_id = self.data.get('workspace_id') + user_id = self.data.get('user_id') + + WorkspaceUserResourcePermission( + target=resource_id, + auth_target_type=auth_target_type, + permission_list=[ResourcePermission.VIEW, + ResourcePermission.MANAGE], + workspace_id=workspace_id, + user_id=user_id, + auth_type=ResourceAuthType.RESOURCE_PERMISSION_GROUP + ).save() + # 刷新缓存 + version = Cache_Version.PERMISSION_LIST.get_version() + key = Cache_Version.PERMISSION_LIST.get_key(user_id=user_id) + cache.delete(key, version=version) + return True + + def list(self, instance, user, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + UserResourcePermissionUserListRequest(data=instance).is_valid(raise_exception=True) + workspace_id = self.data.get("workspace_id") + user_group_id = self.data.get("user_group_id") + # 用户权限列表 + user_resource_permission_list = native_search(self.get_queryset(instance), get_file_content( + os.path.join(PROJECT_DIR, "apps", "system_manage", 'sql', sql_map.get(self.data.get('auth_target_type'))))) + + return [{**user_resource_permission} + for user_resource_permission in user_resource_permission_list] + + def page(self, instance, current_page: int, page_size: int, user, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + UserResourcePermissionUserListRequest(data=instance).is_valid(raise_exception=True) + workspace_id = self.data.get("workspace_id") + user_group_id = self.data.get("user_group_id") + # 用户组对应的资源权限分页列表 + user_resource_permission_page_list = native_page_search(current_page, page_size, self.get_queryset(instance), + get_file_content( + os.path.join(PROJECT_DIR, "apps", "system_manage", + 'sql', sql_map.get( + self.data.get('auth_target_type'))) + )) + + return user_resource_permission_page_list + + def edit(self, instance, user, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + UpdateUserResourcePermissionRequest(data={'user_resource_permission_list': instance}).is_valid( + raise_exception=True, + auth_target_type=self.data.get( + 'auth_target_type'), + workspace_id=self.data.get('workspace_id')) + workspace_id = self.data.get("workspace_id") + user_group_id = self.data.get("user_group_id") + update_list = [] + save_list = [] + targets = [item['target_id'] for item in instance] + QuerySet(WorkspaceUserGroupResourcePermission).filter( + workspace_id=workspace_id, + user_group_id=user_group_id, + auth_target_type=self.data.get('auth_target_type'), + target__in=targets + ).delete() + workspace_user_resource_permission_exist_list = [] + for user_resource_permission in instance: + permission = user_resource_permission['permission'] + auth_type, permission_list = permission_map[permission] + exist_list = [user_resource_permission_exist for user_resource_permission_exist in + workspace_user_resource_permission_exist_list if + user_resource_permission.get('target_id') == str(user_resource_permission_exist.target)] + if len(exist_list) > 0: + exist_list[0].permission_list = [key for key in user_resource_permission.get('permission').keys() if + user_resource_permission.get('permission').get(key)] + exist_list[0].auth_type = user_resource_permission.get('auth_type') + update_list.append(exist_list[0]) + else: + save_list.append(WorkspaceUserGroupResourcePermission(target=user_resource_permission.get('target_id'), + auth_target_type=self.data.get('auth_target_type'), + permission_list=permission_list, + workspace_id=workspace_id, + user_group_id=user_group_id, + auth_type=auth_type)) + # 批量更新 + QuerySet(WorkspaceUserGroupResourcePermission).bulk_update(update_list, + ['permission_list', 'auth_type']) if len( + update_list) > 0 else None + # 批量插入 + QuerySet(WorkspaceUserGroupResourcePermission).bulk_create(save_list) if len(save_list) > 0 else None + version = Cache_Version.PERMISSION_LIST.get_version() + key = Cache_Version.PERMISSION_LIST.get_key(user_group_id=user_group_id) + cache.delete(key, version=version) + return instance + + +class ResourceUserPermissionUserListRequest(serializers.Serializer): + name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('Name')) + permission = serializers.MultipleChoiceField(required=False, allow_null=True, allow_blank=True, + choices=['NOT_AUTH', 'MANAGE', 'VIEW', 'ROLE'], + label=_('permission')) + + +class ResourceUserPermissionEditRequest(serializers.Serializer): + user_group_id = serializers.CharField(required=True, label=_('User Group id')) + permission = serializers.ChoiceField(required=True, choices=['NOT_AUTH', 'MANAGE', 'VIEW', 'ROLE'], + label=_('permission')) + + +permission_map = { + "ROLE": ("ROLE", ["ROLE"]), + "MANAGE": ("RESOURCE_PERMISSION_GROUP", ["MANAGE", "VIEW"]), + "VIEW": ("RESOURCE_PERMISSION_GROUP", ["VIEW"]), + "NOT_AUTH": ("RESOURCE_PERMISSION_GROUP", []), +} + + +class ResourceUserGroupPermissionSerializer(serializers.Serializer): + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + target = serializers.CharField(required=True, label=_('resource id')) + auth_target_type = serializers.CharField(required=True, label=_('resource')) + users_permission = ResourceUserPermissionEditRequest(required=False, many=True, label=_('users_permission')) + + RESOURCE_MODEL_MAP = { + 'APPLICATION': Application, + 'KNOWLEDGE': Knowledge, + 'TOOL': Tool + } + + def get_queryset(self, instance): + + user_query_set = QuerySet(model=get_dynamics_model({ + 'name': models.CharField(), + "permission": models.CharField(), + "u.id": models.UUIDField(), + })) + name = instance.get('name') + permission = instance.get('permission') + query_p_list = [None if p == "NOT_AUTH" else p for p in permission] + + workspace_user_resource_permission_query_set = QuerySet(WorkspaceUserGroupResourcePermission).filter( + workspace_id=self.data.get('workspace_id'), + auth_target_type=self.data.get('auth_target_type'), + target=self.data.get('target')) + if name: + user_query_set = user_query_set.filter(name__contains=name) + if permission: + if all([p is None for p in query_p_list]): + user_query_set = user_query_set.filter( + permission=None) + else: + if any([p is None for p in query_p_list]): + user_query_set = user_query_set.filter( + Q(permission__in=query_p_list) | Q(permission=None)) + else: + user_query_set = user_query_set.filter( + permission__in=query_p_list) + return { + 'workspace_user_resource_permission_query_set': workspace_user_resource_permission_query_set, + 'user_query_set': user_query_set + } + + def list(self, instance, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + ResourceUserPermissionUserListRequest(data=instance).is_valid(raise_exception=True) + # 资源的用户授权列表 + resource_user_permission_list = native_search(self.get_queryset(instance), get_file_content( + os.path.join(PROJECT_DIR, "apps", "system_manage", + 'sql', + 'get_resource_user_group_permission_detail.sql' + ) + )) + return resource_user_permission_list + + + def page(self, instance, current_page: int, page_size: int, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + ResourceUserPermissionUserListRequest(data=instance).is_valid(raise_exception=True) + resource_user_permission_page_list = native_page_search(current_page, page_size, + self.get_queryset(instance), + get_file_content( + os.path.join(PROJECT_DIR, "apps", "system_manage", + 'sql', + "get_resource_user_group_permission_detail.sql" + ) + )) + return resource_user_permission_page_list + + def get_has_manage_permission_resource_under_folders(self, current_user_id, folder_ids): + + workspace_id = self.data.get("workspace_id") + auth_target_type = self.data.get("auth_target_type") + resource_model = self.RESOURCE_MODEL_MAP[auth_target_type] + + from folders.serializers.folder import has_exact_permission_by_role + + permission_id = f"{auth_target_type}:READ+AUTH" + + role_type = RoleConstants.USER.value.__str__() + has_user_role_exact_permission = has_exact_permission_by_role(current_user_id, workspace_id, permission_id, + role_type) + + permission_list = ['MANAGE'] + if has_user_role_exact_permission: + permission_list = ['MANAGE', 'ROLE'] + + current_user_managed_resources_ids = QuerySet(WorkspaceUserGroupResourcePermission).filter( + workspace_id=workspace_id, user_id=current_user_id, auth_target_type=auth_target_type, + target__in=QuerySet(resource_model).filter(workspace_id=workspace_id, folder__in=folder_ids).annotate( + id_str=Cast('id', TextField()) + ).values_list("id_str", flat=True), + permission_list__overlap=permission_list).values_list('target', flat=True) + + return current_user_managed_resources_ids + + def edit(self, instance, with_valid=True, current_user_id=None): + if with_valid: + self.is_valid(raise_exception=True) + ResourceUserPermissionEditRequest(data=instance, many=True).is_valid( + raise_exception=True) + + workspace_id = self.data.get("workspace_id") + target = self.data.get("target") + auth_target_type = self.data.get("auth_target_type") + users_permission = instance + + user_group_ids = [item["user_group_id"] for item in users_permission] + include_children = users_permission[0].get('include_children') + folder_ids = users_permission[0].get('folder_ids') + # 删除已存在的对应的用户在该资源下的权限 + + if include_children: + managed_resource_ids = list( + self.get_has_manage_permission_resource_under_folders(current_user_id, folder_ids, )) + folder_ids + + else: + managed_resource_ids = [target] + QuerySet(WorkspaceUserGroupResourcePermission).filter( + workspace_id=workspace_id, + target__in=managed_resource_ids, + auth_target_type=auth_target_type, + user_group_id__in=user_group_ids + ).delete() + + save_list = [ + WorkspaceUserGroupResourcePermission( + target=resource_id, + auth_target_type=auth_target_type, + workspace_id=workspace_id, + auth_type=permission_map[item['permission']][0], + user_group_id=item["user_group_id"], + permission_list=permission_map[item['permission']][1] + ) + for resource_id in managed_resource_ids + for item in users_permission + ] + + if save_list: + QuerySet(WorkspaceUserResourcePermission).bulk_create(save_list) + + version = Cache_Version.PERMISSION_LIST.get_version() + for user_group_id in user_group_ids: + key = Cache_Version.PERMISSION_LIST.get_key(user_group_id=user_group_id) + cache.delete(key, version=version) + return instance diff --git a/apps/system_manage/sql/get_application_user_group_resource_permission.sql b/apps/system_manage/sql/get_application_user_group_resource_permission.sql new file mode 100644 index 00000000000..f6193500f54 --- /dev/null +++ b/apps/system_manage/sql/get_application_user_group_resource_permission.sql @@ -0,0 +1,47 @@ +SELECT resource_or_folder.*, + CASE + WHEN wurp.permission IS NULL THEN 'NOT_AUTH' + ELSE wurp.permission + END +FROM ( + SELECT id::text, + "name", + 'APPLICATION' AS "auth_target_type", + 'application' AS "resource_type", + user_group_id, + workspace_id, + icon, + folder_id, + create_time + FROM application + ${query_set} + UNION + SELECT application_folder."id"::text, + application_folder."name", + 'APPLICATION' AS "auth_target_type", + 'folder' AS "resource_type", + application_folder."user_group_id", + application_folder."workspace_id", + NULL AS "icon", + application_folder."parent_id" AS "folder_id", + application_folder."create_time" + FROM application_folder + ${folder_query_set} + ) resource_or_folder +LEFT JOIN ( + SELECT target, + CASE + WHEN auth_type = 'ROLE' + AND 'ROLE' = ANY (permission_list) THEN 'ROLE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'MANAGE' = ANY (permission_list) THEN 'MANAGE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'VIEW' = ANY (permission_list) THEN 'VIEW' + ELSE NULL + END AS permission + FROM workspace_user_group_resource_permission + ${workspace_user_group_resource_permission_query_set} +) wurp +ON wurp.target::text = resource_or_folder.id +${resource_query_set} +ORDER BY resource_or_folder.create_time DESC diff --git a/apps/system_manage/sql/get_knowledge_user_group_resource_permission.sql b/apps/system_manage/sql/get_knowledge_user_group_resource_permission.sql new file mode 100644 index 00000000000..f2a6a3ca251 --- /dev/null +++ b/apps/system_manage/sql/get_knowledge_user_group_resource_permission.sql @@ -0,0 +1,50 @@ +SELECT resource_or_folder.*, + CASE + WHEN wurp.permission IS NULL THEN 'NOT_AUTH' + ELSE wurp.permission + END +FROM ( + SELECT + id::text, + "name", + 'KNOWLEDGE' AS "auth_target_type", + 'knowledge' AS "resource_type", + user_group_id, + workspace_id, + "type"::varchar AS "icon", + folder_id, + create_time + FROM knowledge + ${query_set} + UNION + SELECT knowledge_folder."id"::text, + knowledge_folder."name", + 'KNOWLEDGE' AS "auth_target_type", + 'folder' AS "resource_type", + knowledge_folder."user_group_id", + knowledge_folder."workspace_id", + NULL AS "icon", + knowledge_folder."parent_id" AS "folder_id", + knowledge_folder."create_time" + FROM knowledge_folder + ${folder_query_set} + ) resource_or_folder +LEFT JOIN ( + SELECT + target, + CASE + WHEN auth_type = 'ROLE' + AND 'ROLE' = ANY(permission_list) THEN 'ROLE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'MANAGE' = ANY(permission_list) THEN 'MANAGE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'VIEW' = ANY(permission_list) THEN 'VIEW' + ELSE null + END AS permission + FROM + workspace_user_group_resource_permission + ${workspace_user_group_resource_permission_query_set} +) wurp +ON wurp.target::text = resource_or_folder.id +${resource_query_set} +ORDER BY resource_or_folder.create_time DESC \ No newline at end of file diff --git a/apps/system_manage/sql/get_model_user_group_resource_permission.sql b/apps/system_manage/sql/get_model_user_group_resource_permission.sql new file mode 100644 index 00000000000..e5c8f3d7057 --- /dev/null +++ b/apps/system_manage/sql/get_model_user_group_resource_permission.sql @@ -0,0 +1,55 @@ +SELECT + resource_or_folder.*, + CASE + WHEN + wurp."permission" is null then 'NOT_AUTH' + ELSE wurp."permission" + END +FROM ( + SELECT + "id"::text, + "name", + 'MODEL' AS "auth_target_type", + 'model' AS "resource_type", + user_group_id, + workspace_id, + provider as icon, + 'default' as folder_id, + create_time + FROM + model + ${query_set} + UNION + SELECT + "id"::text, + "name", + 'MODEL' AS "auth_target_type", + 'folder' AS "resource_type", + user_group_id, + workspace_id, + provider as icon, + 'default' as folder_id, + create_time + FROM model + ${folder_query_set} + AND 1=0 +) resource_or_folder +LEFT JOIN ( + SELECT + target, + CASE + WHEN auth_type = 'ROLE' + AND 'ROLE' = ANY(permission_list) THEN 'ROLE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'MANAGE' = ANY(permission_list) THEN 'MANAGE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'VIEW' = ANY(permission_list) THEN 'VIEW' + ELSE null + END AS permission + FROM + workspace_user_group_resource_permission + ${workspace_user_group_resource_permission_query_set} +) wurp +ON wurp.target = resource_or_folder."id" +${resource_query_set} +ORDER BY resource_or_folder.create_time DESC \ No newline at end of file diff --git a/apps/system_manage/sql/get_resource_user_group_permission_detail.sql b/apps/system_manage/sql/get_resource_user_group_permission_detail.sql new file mode 100644 index 00000000000..67541199bf8 --- /dev/null +++ b/apps/system_manage/sql/get_resource_user_group_permission_detail.sql @@ -0,0 +1,29 @@ +SELECT + u.id, + u.name, + case + when + wurp."permission" is null then 'NOT_AUTH' + else wurp."permission" + end +FROM + public."system_user_group" u +LEFT JOIN ( + SELECT + user_group_id , + (case + when auth_type = 'ROLE' + and 'ROLE' = any( permission_list) then 'ROLE' + when auth_type = 'RESOURCE_PERMISSION_GROUP' + and 'MANAGE'= any(permission_list) then 'MANAGE' + when auth_type = 'RESOURCE_PERMISSION_GROUP' + and 'VIEW' = any( permission_list) then 'VIEW' + else null + end) as "permission" + FROM + workspace_user_group_resource_permission + ${workspace_user_group_resource_permission_query_set} + ) wurp +ON + u.id = wurp.user_group_id +${user_query_set} \ No newline at end of file diff --git a/apps/system_manage/sql/get_tool_user_group_resource_permission.sql b/apps/system_manage/sql/get_tool_user_group_resource_permission.sql new file mode 100644 index 00000000000..3da90bded52 --- /dev/null +++ b/apps/system_manage/sql/get_tool_user_group_resource_permission.sql @@ -0,0 +1,51 @@ +SELECT resource_or_folder.*, + CASE + WHEN wurp."permission" IS NULL THEN 'NOT_AUTH' + ELSE wurp."permission" + END +FROM ( + SELECT "id"::text, + "name", + 'TOOL' AS "auth_target_type", + 'tool' AS "resource_type", + user_group_id, + workspace_id, + icon, + folder_id, + tool_type, + create_time + FROM tool + ${query_set} + UNION + SELECT tool_folder."id"::text, + tool_folder."name", + 'TOOL' AS "auth_target_type", + 'folder' AS "resource_type", + tool_folder."user_group_id", + tool_folder."workspace_id", + NULL AS "icon", + tool_folder."parent_id" AS "folder_id", + NULL AS "tool_type", + tool_folder."create_time" + FROM tool_folder + ${folder_query_set} + ) resource_or_folder +LEFT JOIN ( + SELECT target, + CASE + WHEN auth_type = 'ROLE' + AND 'ROLE' = ANY(permission_list) THEN 'ROLE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'MANAGE' = ANY(permission_list) THEN 'MANAGE' + WHEN auth_type = 'RESOURCE_PERMISSION_GROUP' + AND 'VIEW' = ANY(permission_list) THEN 'VIEW' + ELSE null + END AS permission + FROM + workspace_user_group_resource_permission + ${workspace_user_group_resource_permission_query_set} +) wurp +ON wurp.target::text = resource_or_folder."id" +${resource_query_set} +ORDER BY resource_or_folder.create_time DESC + diff --git a/apps/system_manage/urls.py b/apps/system_manage/urls.py index 6685bcc3ab7..eba3e147583 100644 --- a/apps/system_manage/urls.py +++ b/apps/system_manage/urls.py @@ -8,8 +8,12 @@ urlpatterns = [ path('workspace//user_resource_permission/user//resource/', views.WorkSpaceUserResourcePermissionView.as_view()), path('workspace//user_resource_permission/user//resource///', views.WorkSpaceUserResourcePermissionView.Page.as_view()), + path('workspace//user_group_resource_permission/user_group//resource/', views.WorkSpaceUserGroupResourcePermissionView.as_view()), + path('workspace//user_group_resource_permission/user_group//resource///', views.WorkSpaceUserGroupResourcePermissionView.Page.as_view()), path('workspace//resource_user_permission/resource//resource/', views.WorkspaceResourceUserPermissionView.as_view()), path('workspace//resource_user_permission/resource//resource///', views.WorkspaceResourceUserPermissionView.Page.as_view()), + path('workspace//resource_user_group_permission/resource//resource/', views.WorkspaceResourceUserGroupPermissionView.as_view()), + path('workspace//resource_user_group_permission/resource//resource///', views.WorkspaceResourceUserGroupPermissionView.Page.as_view()), path('workspace//resource_mapping////', views.ResourceMappingView.as_view()), path('workspace//mapping_resource////', views.MappingResourceView.as_view()), path('email_setting', views.SystemSetting.Email.as_view()), diff --git a/apps/system_manage/views/__init__.py b/apps/system_manage/views/__init__.py index 07d2ac17d65..d78a56454bf 100644 --- a/apps/system_manage/views/__init__.py +++ b/apps/system_manage/views/__init__.py @@ -12,3 +12,4 @@ from .valid import * from .resource_mapping import * from .system_chat_user import * +from .user_group_resource_permission import * diff --git a/apps/system_manage/views/user_group_resource_permission.py b/apps/system_manage/views/user_group_resource_permission.py new file mode 100644 index 00000000000..d8a0bede6b3 --- /dev/null +++ b/apps/system_manage/views/user_group_resource_permission.py @@ -0,0 +1,202 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: workspace_user_resource_permission.py + @date:2025/4/28 16:38 + @desc: +""" +from django.db.models import QuerySet +from django.utils.translation import gettext_lazy as _ +from drf_spectacular.utils import extend_schema +from rest_framework.request import Request +from rest_framework.views import APIView + +from common import result +from common.auth import TokenAuth +from common.auth.authentication import has_permissions +from common.constants.permission_constants import RoleConstants, Permission, Group, Operate, ViewPermission, \ + CompareConstants +from common.log.log import log +from system_manage.api.user_resource_permission import UserResourcePermissionAPI, EditUserResourcePermissionAPI, \ + ResourceUserPermissionAPI, ResourceUserPermissionPageAPI, ResourceUserPermissionEditAPI, \ + UserResourcePermissionPageAPI +from system_manage.serializers.user_group_resource_permission import UserGroupResourcePermissionSerializer, \ + ResourceUserGroupPermissionSerializer +from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer, \ + ResourceUserPermissionSerializer +from users.models.user_group import SystemUserGroup + + +def get_user_operation_object(user_group_id): + user_group = QuerySet(model=SystemUserGroup).filter(id=user_group_id).first() + if user_group is not None: + return { + "name": user_group.name + } + return {} + + +class WorkSpaceUserGroupResourcePermissionView(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Obtain resource authorization list'), + operation_id=_('Obtain resource authorization list'), # type: ignore + parameters=UserResourcePermissionAPI.get_parameters(), + responses=UserResourcePermissionAPI.get_response(), + tags=[_('Resources authorization')] # type: ignore + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource') + '_WORKSPACE_USER_RESOURCE_PERMISSION'), + operate=Operate.READ), + RoleConstants.ADMIN, RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, user_group_id: str, resource: str): + return result.success(UserGroupResourcePermissionSerializer( + data={'workspace_id': workspace_id, 'user_group_id': user_group_id, 'auth_target_type': resource} + ).list({'name': request.query_params.get('name'), + 'permission': request.query_params.getlist('permission[]')}, request.user)) + + @extend_schema( + methods=['PUT'], + description=_('Modify the resource authorization list'), + operation_id=_('Modify the resource authorization list'), # type: ignore + parameters=EditUserResourcePermissionAPI.get_parameters(), + request=EditUserResourcePermissionAPI.get_request(), + responses=EditUserResourcePermissionAPI.get_response(), + tags=[_('Resources authorization')] # type: ignore + ) + @log(menu='System', operate='Modify the resource authorization list', + get_operation_object=lambda r, k: get_user_operation_object(k.get('user_group_id')) + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource') + '_WORKSPACE_USER_RESOURCE_PERMISSION'), + operate=Operate.EDIT), + RoleConstants.ADMIN, RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def put(self, request: Request, workspace_id: str, user_group_id: str, resource: str): + return result.success(UserGroupResourcePermissionSerializer( + data={'workspace_id': workspace_id, 'user_group_id': user_group_id, 'auth_target_type': resource} + ).edit(request.data, request.user)) + + class Page(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Obtain resource authorization list by page'), + summary=_('Obtain resource authorization list by page'), + operation_id=_('Obtain resource authorization list by page'), # type: ignore + request=None, + parameters=UserResourcePermissionPageAPI.get_parameters(), + responses=UserResourcePermissionPageAPI.get_response(), + tags=[_('Resources authorization')] # type: ignore + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource') + '_WORKSPACE_USER_RESOURCE_PERMISSION'), + operate=Operate.READ), + RoleConstants.ADMIN, RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, user_group_id: str, resource: str, current_page: str, + page_size: str): + return result.success(UserGroupResourcePermissionSerializer( + data={'workspace_id': workspace_id, 'user_group_id': user_group_id, 'auth_target_type': resource} + ).page({'name': request.query_params.get('name'), + 'permission': request.query_params.getlist('permission[]')}, current_page, page_size, request.user)) + + +class WorkspaceResourceUserGroupPermissionView(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Get user authorization status of resource'), + summary=_('Get user authorization status of resource'), + operation_id=_('Get user authorization status of resource'), # type: ignore + parameters=ResourceUserPermissionAPI.get_parameters(), + responses=ResourceUserPermissionAPI.get_response(), + tags=[_('Resources authorization')] # type: ignore + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.AUTH, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}:ROLE/WORKSPACE_MANAGE"), + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.AUTH, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource').replace('_FOLDER','')}/{kwargs.get('target')}"), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [lambda r, kwargs: Permission(group=Group(kwargs.get('resource').replace('_FOLDER','')), + operate=Operate.SELF, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource').replace('_FOLDER','')}/{kwargs.get('target')}")], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, target: str, resource: str): + return result.success(UserGroupResourcePermissionSerializer( + data={'workspace_id': workspace_id, "target": target, 'auth_target_type': resource.replace('_FOLDER',''), + }).list( + {'name': request.query_params.get("name"), + 'permission': request.query_params.getlist("permission[]") + })) + + @extend_schema( + methods=['PUT'], + description=_('Edit user authorization status of resource'), + summary=_('Edit user authorization status of resource'), + operation_id=_('Edit user authorization status of resource'), # type: ignore + parameters=ResourceUserPermissionEditAPI.get_parameters(), + request=ResourceUserPermissionEditAPI.get_request(), + responses=ResourceUserPermissionEditAPI.get_response(), + tags=[_('Resources authorization')] # type: ignore + ) + @log(menu='System', operate='Edit user authorization status of resource', + get_operation_object=lambda r, k: get_user_operation_object(k.get('user_id')) + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.AUTH, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}:ROLE/WORKSPACE_MANAGE"), + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.AUTH, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource').replace('_FOLDER','')}/{kwargs.get('target')}"), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [lambda r, kwargs: Permission(group=Group(kwargs.get('resource').replace('_FOLDER','')), + operate=Operate.SELF, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource').replace('_FOLDER','')}/{kwargs.get('target')}")], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def put(self, request: Request, workspace_id: str, target: str, resource: str): + return result.success(ResourceUserGroupPermissionSerializer( + data={'workspace_id': workspace_id, "target": target, 'auth_target_type': resource.replace('_FOLDER',''), }) + .edit(instance=request.data, current_user_id=request.user.id)) + + class Page(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Get user authorization status of resource by page'), + summary=_('Get user authorization status of resource by page'), + operation_id=_('Get user authorization status of resource by page'), # type: ignore + parameters=ResourceUserPermissionPageAPI.get_parameters(), + responses=ResourceUserPermissionPageAPI.get_response(), + tags=[_('Resources authorization')] # type: ignore + ) + @has_permissions( + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.AUTH, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}:ROLE/WORKSPACE_MANAGE"), + lambda r, kwargs: Permission(group=Group(kwargs.get('resource')), + operate=Operate.AUTH, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource').replace('_FOLDER','')}/{kwargs.get('target')}"), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [lambda r, kwargs: Permission(group=Group(kwargs.get('resource').replace('_FOLDER','')), + operate=Operate.SELF, + resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}/{kwargs.get('resource').replace('_FOLDER','')}/{kwargs.get('target')}")], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, target: str, resource: str, current_page: int, + page_size: int): + return result.success(ResourceUserGroupPermissionSerializer( + data={'workspace_id': workspace_id, "target": target, 'auth_target_type': resource.replace('_FOLDER',''), } + ).page({'name': request.query_params.get("name"), + 'permission': request.query_params.getlist("permission[]")}, current_page, page_size, + ))