From 919c4e3f7f32f68912de35e2c18f1ac3aa6103f1 Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Mon, 11 Aug 2025 15:51:34 +0800 Subject: [PATCH] feat: Modify the authorization information of resources for users --- .../api/user_resource_permission.py | 179 +++++++++++++++++- .../serializers/user_resource_permission.py | 124 +++++++++++- .../get_resource_user_permission_detail.sql | 30 +++ apps/system_manage/urls.py | 2 + .../views/user_resource_permission.py | 63 +++++- 5 files changed, 393 insertions(+), 5 deletions(-) create mode 100644 apps/system_manage/sql/get_resource_user_permission_detail.sql diff --git a/apps/system_manage/api/user_resource_permission.py b/apps/system_manage/api/user_resource_permission.py index 4af8da325..c902dd8ee 100644 --- a/apps/system_manage/api/user_resource_permission.py +++ b/apps/system_manage/api/user_resource_permission.py @@ -8,11 +8,13 @@ """ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter +from rest_framework import serializers +from django.utils.translation import gettext_lazy as _ from common.mixins.api_mixin import APIMixin -from common.result import ResultSerializer +from common.result import ResultSerializer, ResultPageSerializer from system_manage.serializers.user_resource_permission import UserResourcePermissionResponse, \ - UpdateUserResourcePermissionRequest + UpdateUserResourcePermissionRequest, ResourceUserPermissionEditRequest class APIUserResourcePermissionResponse(ResultSerializer): @@ -49,3 +51,176 @@ class EditUserResourcePermissionAPI(APIMixin): @staticmethod def get_request(): return UpdateUserResourcePermissionRequest() + + +class ResourceUserPermissionResponse(serializers.Serializer): + id = serializers.CharField(required=True, label=_('user id')) + nick_name = serializers.CharField(required=True, allow_null=True, allow_blank=True, label=_('nick_name')) + username = serializers.CharField(required=True, allow_null=True, allow_blank=True, label=_('username')) + permission = serializers.CharField(required=True, label=_('permission')) + + +class APIResourceUserPermissionResponse(ResultSerializer): + def get_data(self): + return ResourceUserPermissionResponse(many=True) + + +class ResourceUserPermissionAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="target", + description="资源id", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="resource", + description="资源类型", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="username", + description="用户名", + type=OpenApiTypes.STR, + location='query', + required=False + ), + OpenApiParameter( + name="nick_name", + description="姓名", + type=OpenApiTypes.STR, + location='query', + required=False + ), + OpenApiParameter( + name="permission", + description="权限", + type=OpenApiTypes.STR, + location='query', + required=False + ), + ] + + @staticmethod + def get_response(): + return APIResourceUserPermissionResponse + + +class APIResourceUserPermissionPageResponse(ResultPageSerializer): + def get_data(self): + return ResourceUserPermissionResponse(many=True) + + +class ResourceUserPermissionPageAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="target", + description="资源id", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="resource", + description="资源类型", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="current_page", + description=_("Current page"), + type=OpenApiTypes.INT, + location='path', + required=True, + ), + OpenApiParameter( + name="page_size", + description=_("Page size"), + type=OpenApiTypes.INT, + location='path', + required=True, + ), + OpenApiParameter( + name="username", + description="用户名", + type=OpenApiTypes.STR, + location='query', + required=False + ), + OpenApiParameter( + name="nick_name", + description="姓名", + type=OpenApiTypes.STR, + location='query', + required=False + ), + OpenApiParameter( + name="permission", + description="权限", + type=OpenApiTypes.STR, + location='query', + required=False + ), + ] + + @staticmethod + def get_response(): + return APIResourceUserPermissionPageResponse + + + +class ResourceUserPermissionEditAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="target", + description="资源id", + type=OpenApiTypes.STR, + location='path', + required=True + ), + OpenApiParameter( + name="resource", + description="资源类型", + type=OpenApiTypes.STR, + location='path', + required=True + ), + ] + @staticmethod + def get_request(): + return ResourceUserPermissionEditRequest(required=True, many=True, label=_('users_permission')) + + @staticmethod + def get_response(): + return APIResourceUserPermissionResponse() \ No newline at end of file diff --git a/apps/system_manage/serializers/user_resource_permission.py b/apps/system_manage/serializers/user_resource_permission.py index 9e50b1f77..a584c5738 100644 --- a/apps/system_manage/serializers/user_resource_permission.py +++ b/apps/system_manage/serializers/user_resource_permission.py @@ -10,6 +10,7 @@ import json import os from django.core.cache import cache +from django.db import models from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from rest_framework import serializers @@ -19,7 +20,7 @@ from common.constants.cache_version import Cache_Version from common.constants.permission_constants import get_default_workspace_user_role_mapping_list, RoleConstants, \ ResourcePermission, ResourcePermissionRole, ResourceAuthType from common.database_model_manage.database_model_manage import DatabaseModelManage -from common.db.search import native_search +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 @@ -30,6 +31,7 @@ from maxkb.settings import edition from models_provider.models import Model from system_manage.models import WorkspaceUserResourcePermission, AuthTargetType from tools.models import Tool +from users.models import User from users.serializers.user import is_workspace_manage @@ -260,3 +262,123 @@ class UserResourcePermissionSerializer(serializers.Serializer): key = Cache_Version.PERMISSION_LIST.get_key(user_id=user_id) cache.delete(key, version=version) return True + + +class ResourceUserPermissionUserListRequest(serializers.Serializer): + nick_name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('workspace id')) + username = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('workspace id')) + permission = serializers.ChoiceField(required=True, choices=['NOT_AUTH', 'MANAGE', 'VIEW', 'ROLE'], + label=_('permission')) + + +class ResourceUserPermissionEditRequest(serializers.Serializer): + user_id = serializers.CharField(required=True, label=_('workspace 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 ResourceUserPermissionSerializer(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')) + + def get_queryset(self, instance): + + user_query_set = QuerySet(model=get_dynamics_model({ + 'nick_name': models.CharField(), + 'username': models.CharField(), + "permission": models.CharField(), + })) + nick_name = instance.get('nick_name') + username = instance.get('username') + permission = instance.get('permission') + workspace_user_resource_permission_query_set = QuerySet(WorkspaceUserResourcePermission).filter( + workspace_id=self.data.get('workspace_id'), + auth_target_type=self.data.get('auth_target_type'), + target=self.data.get('target')) + if nick_name: + user_query_set = user_query_set.filter(nick_name__contains=nick_name) + if username: + user_query_set = user_query_set.filter(username__contains=username) + if permission: + user_query_set = user_query_set.filter( + permission=None if instance.get('permission') == 'NOT_AUTH' else instance.get('permission')) + + 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_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_permission_detail.sql') + )) + return resource_user_permission_page_list + + def edit(self, instance, with_valid=True): + 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 + + users_id = [item["user_id"] for item in users_permission] + # 删除已存在的对应的用户在该资源下的权限 + QuerySet(WorkspaceUserResourcePermission).filter( + workspace_id=workspace_id, + target=target, + auth_target_type=auth_target_type, + user_id__in=users_id + ).delete() + + save_list = [] + for item in users_permission: + permission = item['permission'] + auth_type, permission_list = permission_map[permission] + + save_list.append(WorkspaceUserResourcePermission( + target=target, + auth_target_type=auth_target_type, + workspace_id=workspace_id, + auth_type=auth_type, + user_id=item["user_id"], + permission_list=permission_list + )) + if save_list: + QuerySet(WorkspaceUserResourcePermission).bulk_create(save_list) + + version = Cache_Version.PERMISSION_LIST.get_version() + for user_id in users_id: + key = Cache_Version.PERMISSION_LIST.get_key(user_id=user_id) + cache.delete(key, version=version) + return True diff --git a/apps/system_manage/sql/get_resource_user_permission_detail.sql b/apps/system_manage/sql/get_resource_user_permission_detail.sql new file mode 100644 index 000000000..6298930e8 --- /dev/null +++ b/apps/system_manage/sql/get_resource_user_permission_detail.sql @@ -0,0 +1,30 @@ +SELECT + u.id, + u.nick_name, + u.username, + case + when + wurp."permission" is null then 'NOT_AUTH' + else wurp."permission" + end +FROM + public."user" u +LEFT JOIN ( + SELECT + user_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 'NO_AUTH' + end) as "permission" + FROM + workspace_user_resource_permission + ${workspace_user_resource_permission_query_set} + ) wurp +ON + u.id = wurp.user_id +${user_query_set} \ No newline at end of file diff --git a/apps/system_manage/urls.py b/apps/system_manage/urls.py index e1c41d7c4..6a746fbdd 100644 --- a/apps/system_manage/urls.py +++ b/apps/system_manage/urls.py @@ -6,6 +6,8 @@ app_name = "system_manage" # @formatter:off urlpatterns = [ path('workspace//user_resource_permission/user//resource/', views.WorkSpaceUserResourcePermissionView.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('email_setting', views.SystemSetting.Email.as_view()), path('profile', views.SystemProfile.as_view()), path('valid//', views.Valid.as_view()) diff --git a/apps/system_manage/views/user_resource_permission.py b/apps/system_manage/views/user_resource_permission.py index da38106db..f8c7167af 100644 --- a/apps/system_manage/views/user_resource_permission.py +++ b/apps/system_manage/views/user_resource_permission.py @@ -18,8 +18,10 @@ from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants, RoleConstants, Permission, Group, Operate from common.log.log import log from common.result import DefaultResultSerializer -from system_manage.api.user_resource_permission import UserResourcePermissionAPI, EditUserResourcePermissionAPI -from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer +from system_manage.api.user_resource_permission import UserResourcePermissionAPI, EditUserResourcePermissionAPI, \ + ResourceUserPermissionAPI, ResourceUserPermissionPageAPI, ResourceUserPermissionEditAPI +from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer, \ + ResourceUserPermissionSerializer from users.models import User @@ -72,3 +74,60 @@ class WorkSpaceUserResourcePermissionView(APIView): return result.success(UserResourcePermissionSerializer( data={'workspace_id': workspace_id, 'user_id': user_id, 'auth_target_type': resource} ).edit(request.data, request.user)) + + +class WorkspaceResourceUserPermissionView(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 + ) + def get(self, request: Request, workspace_id: str, target: str, resource: str): + return result.success(ResourceUserPermissionSerializer( + data={'workspace_id': workspace_id, "target": target, 'auth_target_type': resource, + }).list( + {'username': request.query_params.get("username"), 'nick_name': request.query_params.get("nick_name"), + 'permission': request.query_params.get("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 + ) + def put(self, request: Request, workspace_id: str, target: str, resource: str): + + return result.success(ResourceUserPermissionSerializer( + data={'workspace_id': workspace_id, "target": target, 'auth_target_type': resource, }) + .edit(instance=request.data)) + + 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 + ) + def get(self, request: Request, workspace_id: str, target: str, resource: str, current_page: int, + page_size: int): + return result.success(ResourceUserPermissionSerializer( + data={'workspace_id': workspace_id, "target": target, 'auth_target_type': resource, } + ).page({'username': request.query_params.get("username"), + 'nick_name': request.query_params.get("nick_name"), 'permission': request.query_params.get("permission")}, current_page, page_size, + ))