feat: Resources authorization (#3039)

This commit is contained in:
shaohuzhang1 2025-05-06 18:35:11 +08:00 committed by GitHub
parent bd865ceafc
commit 115e11052c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 321 additions and 4 deletions

View File

@ -101,14 +101,14 @@ def get_workspace_resource_permission_list_by_workspace_user_permission(
workspace_user_resource_permission.workspace_id)]
role_permission_mapping_list = reduce(lambda x, y: [*x, *y], role_permission_mapping_list, [])
# 如果是根据角色
if (workspace_user_resource_permission.auth_target_type == ResourceAuthType.ROLE
if (workspace_user_resource_permission.auth_type == ResourceAuthType.ROLE
and workspace_user_resource_permission.permission_list.__contains__(
ResourcePermissionRole.ROLE)):
return [
f"{role_permission_mapping.permission_id}:/WORKSPACE/{workspace_user_resource_permission.workspace_id}/{workspace_user_resource_permission.auth_target_type}/{workspace_user_resource_permission.target}"
for role_permission_mapping in role_permission_mapping_list]
elif workspace_user_resource_permission.auth_target_type == ResourceAuthType.RESOURCE_PERMISSION_GROUP:
elif workspace_user_resource_permission.auth_type == ResourceAuthType.RESOURCE_PERMISSION_GROUP:
resource_permission_list = [
[
f"{permission}:/WORKSPACE/{workspace_user_resource_permission.workspace_id}/{workspace_user_resource_permission.auth_target_type}/{workspace_user_resource_permission.target}"
@ -136,7 +136,7 @@ def get_permission_list(user,
# 获取工作空间 用户 角色映射数据
workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(user_id=user_id)
workspace_user_role_mapping_dict = group_by(workspace_user_role_mapping_list,
lambda item: item.role_id)
lambda item: item.workspace_id)
# 获取角色权限映射数据
role_permission_mapping_list = QuerySet(role_permission_mapping_model).filter(
role_id__in=[workspace_user_role_mapping.role_id for workspace_user_role_mapping in
@ -168,7 +168,7 @@ def get_permission_list(user,
role_permission_mapping_dict = group_by(role_permission_mapping_list, lambda item: item.role_id)
workspace_user_role_mapping_list = get_default_workspace_user_role_mapping_list([user.role])
workspace_user_role_mapping_dict = group_by(workspace_user_role_mapping_list,
lambda item: item.role_id)
lambda item: item.workspace_id)
# 资源权限
workspace_resource_permission_list = get_workspace_resource_permission_list(
workspace_user_resource_permission_list,

View File

@ -26,6 +26,8 @@ class Group(Enum):
TOOL = "TOOL"
WORKSPACE_USER_RESOURCE_PERMISSION = "WORKSPACE_USER_RESOURCE_PERMISSION"
class Operate(Enum):
"""
@ -227,6 +229,11 @@ class PermissionConstants(Enum):
DOCUMENT_DELETE = Permission(group=Group.KNOWLEDGE, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN,
RoleConstants.USER])
WORKSPACE_USER_RESOURCE_PERMISSION_READ = Permission(group=Group.WORKSPACE_USER_RESOURCE_PERMISSION,
operate=Operate.READ,
role_list=[RoleConstants.ADMIN,
RoleConstants.WORKSPACE_MANAGE])
def get_workspace_application_permission(self):
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
resource_path=

View File

@ -26,6 +26,7 @@ urlpatterns = [
path("api/", include("models_provider.urls")),
path("api/", include("folders.urls")),
path("api/", include("knowledge.urls")),
path("api/", include("system_manage.urls")),
]
urlpatterns += [
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由下面两个ui也是根据这个配置文件来生成的

View File

@ -0,0 +1,8 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file __init__.py
@date2025/4/28 17:05
@desc:
"""

View File

@ -0,0 +1,44 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file workspace_user_resource_permission.py
@date2025/4/28 18:13
@desc:
"""
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter
from common.mixins.api_mixin import APIMixin
from common.result import ResultSerializer
from system_manage.serializers.user_resource_permission import UserResourcePermissionResponse, \
UpdateUserResourcePermissionRequest
class APIUserResourcePermissionResponse(ResultSerializer):
def get_data(self):
return UserResourcePermissionResponse(many=True)
class UserResourcePermissionAPI(APIMixin):
@staticmethod
def get_parameters():
return [
OpenApiParameter(
name="workspace_id",
description="工作空间id",
type=OpenApiTypes.STR,
location='path',
required=True,
)
]
@staticmethod
def get_response():
return APIUserResourcePermissionResponse
class EditUserResourcePermissionAPI(APIMixin):
@staticmethod
def get_request():
return UpdateUserResourcePermissionRequest()

View File

@ -0,0 +1,8 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file __init__.py
@date2025/4/28 17:05
@desc:
"""

View File

@ -0,0 +1,155 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file workspace_user_resource_permission.py
@date2025/4/28 17:17
@desc:
"""
import json
import os
from django.core.cache import cache
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from common.constants.cache_version import Cache_Version
from common.constants.permission_constants import get_default_workspace_user_role_mapping_list, RoleConstants, \
ResourcePermissionGroup, ResourcePermissionRole, ResourceAuthType
from common.database_model_manage.database_model_manage import DatabaseModelManage
from common.db.search import native_search
from common.db.sql_execute import select_list
from common.exception.app_exception import AppApiException
from common.utils.common import get_file_content
from common.utils.split_model import group_by
from knowledge.models import Knowledge
from maxkb.conf import PROJECT_DIR
from system_manage.models import WorkspaceUserResourcePermission, AuthTargetType
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.ChoiceField(required=True, choices=AuthTargetType.choices, label="授权资源")
user_id = serializers.UUIDField(required=True, label="用户id")
auth_type = serializers.ChoiceField(required=True, choices=ResourceAuthType.choices, label="授权类型")
permission = PermissionSerializer()
class UserResourcePermissionResponse(serializers.Serializer):
KNOWLEDGE = UserResourcePermissionItemResponse(many=True)
class UpdateTeamMemberItemPermissionSerializer(serializers.Serializer):
auth_target_type = serializers.ChoiceField(required=True, choices=AuthTargetType.choices, label="授权资源")
target_id = serializers.CharField(required=True, label=_('target id'))
auth_type = serializers.ChoiceField(required=True, choices=ResourceAuthType.choices, label="授权类型")
permission = PermissionSerializer(required=True, many=False)
class UpdateUserResourcePermissionRequest(serializers.Serializer):
user_resource_permission_list = UpdateTeamMemberItemPermissionSerializer(required=True, many=True)
def is_valid(self, *, workspace_id=None, raise_exception=False):
super().is_valid(raise_exception=True)
user_resource_permission_list = 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])
if illegal_target_id_list is not None and len(illegal_target_id_list) > 0:
raise AppApiException(500,
_('Non-existent application|knowledge base id[') + str(illegal_target_id_list) + ']')
class UserResourcePermissionSerializer(serializers.Serializer):
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
def get_queryset(self):
return {
"knowledge_query_set": QuerySet(Knowledge)
.filter(workspace_id=self.data.get('workspace_id')),
'workspace_user_resource_permission_query_set': QuerySet(WorkspaceUserResourcePermission).filter(
workspace_id=self.data.get('workspace_id'))
}
def list(self, user, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
workspace_id = self.data.get("workspace_id")
# 用户权限列表
user_resource_permission_list = native_search(self.get_queryset(), get_file_content(
os.path.join(PROJECT_DIR, "apps", "system_manage", 'sql', 'get_user_resource_permission.sql')))
workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
workspace_model = DatabaseModelManage.get_model("workspace_model")
if workspace_user_role_mapping_model and workspace_model:
workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(user_id=user.id,
workspace_id=workspace_id)
else:
workspace_user_role_mapping_list = get_default_workspace_user_role_mapping_list([user.role])
is_workspace_manage = any(
[workspace_user_role_mapping for workspace_user_role_mapping in workspace_user_role_mapping_list if
workspace_user_role_mapping.role_id == RoleConstants.WORKSPACE_MANAGE.value])
# 如果当前用户是当前工作空间管理员那么就拥有所有权限
if is_workspace_manage:
user_resource_permission_list = list(
map(lambda row: {**row,
'permission': {ResourcePermissionGroup.VIEW.value: True,
ResourcePermissionGroup.MANAGE.value: True,
ResourcePermissionRole.ROLE.value: True}},
user_resource_permission_list))
return group_by([{**user_resource_permission, 'permission': {
permission: True if user_resource_permission.get('permission_list').__contains__(permission) else False for
permission in
[ResourcePermissionGroup.VIEW.value, ResourcePermissionGroup.MANAGE.value,
ResourcePermissionRole.ROLE.value]}}
for user_resource_permission in user_resource_permission_list],
key=lambda item: item.get('auth_target_type'))
def edit(self, instance, user, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
UpdateUserResourcePermissionRequest(data=instance).is_valid(raise_exception=True,
workspace_id=self.data.get('workspace_id'))
workspace_id = self.data.get("workspace_id")
update_list = []
save_list = []
user_resource_permission_list = instance.get('user_resource_permission_list')
workspace_user_resource_permission_exist_list = QuerySet(WorkspaceUserResourcePermission).filter(
workspace_id=workspace_id)
for user_resource_permission in user_resource_permission_list:
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)]
update_list.append(exist_list[0])
else:
save_list.append(WorkspaceUserResourcePermission(target=user_resource_permission.get('target_id'),
auth_target_type=user_resource_permission.get(
'auth_target_type'),
permission_list=[key for key in
user_resource_permission.get(
'permission').keys() if
user_resource_permission.get(
'permission').get(key)],
workspace_id=workspace_id,
user_id=user.id,
auth_type=user_resource_permission.get('auth_type')))
# 批量更新
QuerySet(WorkspaceUserResourcePermission).bulk_update(update_list, ['permission_list']) if len(
update_list) > 0 else None
# 批量插入
QuerySet(WorkspaceUserResourcePermission).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_id=str(user.id))
cache.delete(key, version=version)
return True

View File

@ -0,0 +1,16 @@
SELECT
static_temp."target_id"::text
FROM
(SELECT * FROM json_to_recordset(
%s
) AS x(target_id uuid,auth_target_type text)) static_temp
LEFT JOIN (
SELECT
"id",
'KNOWLEDGE' AS "auth_target_type"
FROM
knowledge
WHERE workspace_id= %s
) "app_and_knowledge_temp"
ON "app_and_knowledge_temp"."id" = static_temp."target_id" and app_and_knowledge_temp."auth_target_type"=static_temp."auth_target_type"
WHERE app_and_knowledge_temp.id is NULL ;

View File

@ -0,0 +1,16 @@
SELECT app_or_knowledge.*,
COALESCE(workspace_user_resource_permission.permission_list,'{}')::varchar[] as permission_list,
COALESCE(workspace_user_resource_permission.auth_type,'ROLE') as auth_type
FROM (SELECT "id",
"name",
'KNOWLEDGE' AS "auth_target_type",
user_id,
workspace_id,
"type" AS "icon"
FROM knowledge
${knowledge_query_set}
) app_or_knowledge
LEFT JOIN (SELECT *
FROM workspace_user_resource_permission
${workspace_user_resource_permission_query_set}) workspace_user_resource_permission
ON workspace_user_resource_permission.target = app_or_knowledge."id";

View File

@ -0,0 +1,8 @@
from django.urls import path
from . import views
app_name = "system_manage"
urlpatterns = [
path('workspace/<str:workspace_id>/user_resource_permission', views.WorkSpaceUserResourcePermissionView.as_view())
]

View File

@ -6,3 +6,4 @@
@date2025/4/16 19:07
@desc:
"""
from .user_resource_permission import *

View File

@ -0,0 +1,53 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file workspace_user_resource_permission.py
@date2025/4/28 16:38
@desc:
"""
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 PermissionConstants
from common.result import DefaultResultSerializer
from system_manage.api.user_resource_permission import UserResourcePermissionAPI, EditUserResourcePermissionAPI
from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer
class WorkSpaceUserResourcePermissionView(APIView):
authentication_classes = [TokenAuth]
@extend_schema(
methods=['GET'],
description=_('Obtain resource authorization list'),
operation_id=_('Obtain resource authorization list'),
parameters=UserResourcePermissionAPI.get_parameters(),
responses=UserResourcePermissionAPI.get_response(),
tags=[_('Resources authorization')]
)
@has_permissions(PermissionConstants.WORKSPACE_USER_RESOURCE_PERMISSION_READ.get_workspace_permission())
def get(self, request: Request, workspace_id: str):
return result.success(UserResourcePermissionSerializer(
data={'workspace_id': workspace_id}
).list(request.user))
@extend_schema(
methods=['PUT'],
description=_('Modify the resource authorization list'),
operation_id=_('Modify the resource authorization list'),
parameters=UserResourcePermissionAPI.get_parameters(),
request=EditUserResourcePermissionAPI.get_request(),
responses=DefaultResultSerializer(),
tags=[_('Resources authorization')]
)
def put(self, request: Request, workspace_id: str):
return result.success(UserResourcePermissionSerializer(
data={'workspace_id': workspace_id}
).edit(request.data, request.user))