Skip to content

Commit 115e110

Browse files
authored
feat: Resources authorization (#3039)
1 parent bd865ce commit 115e110

File tree

12 files changed

+321
-4
lines changed

12 files changed

+321
-4
lines changed

apps/common/auth/handle/impl/user_token.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,14 @@ def get_workspace_resource_permission_list_by_workspace_user_permission(
101101
workspace_user_resource_permission.workspace_id)]
102102
role_permission_mapping_list = reduce(lambda x, y: [*x, *y], role_permission_mapping_list, [])
103103
# 如果是根据角色
104-
if (workspace_user_resource_permission.auth_target_type == ResourceAuthType.ROLE
104+
if (workspace_user_resource_permission.auth_type == ResourceAuthType.ROLE
105105
and workspace_user_resource_permission.permission_list.__contains__(
106106
ResourcePermissionRole.ROLE)):
107107
return [
108108
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}"
109109
for role_permission_mapping in role_permission_mapping_list]
110110

111-
elif workspace_user_resource_permission.auth_target_type == ResourceAuthType.RESOURCE_PERMISSION_GROUP:
111+
elif workspace_user_resource_permission.auth_type == ResourceAuthType.RESOURCE_PERMISSION_GROUP:
112112
resource_permission_list = [
113113
[
114114
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,
136136
# 获取工作空间 用户 角色映射数据
137137
workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(user_id=user_id)
138138
workspace_user_role_mapping_dict = group_by(workspace_user_role_mapping_list,
139-
lambda item: item.role_id)
139+
lambda item: item.workspace_id)
140140
# 获取角色权限映射数据
141141
role_permission_mapping_list = QuerySet(role_permission_mapping_model).filter(
142142
role_id__in=[workspace_user_role_mapping.role_id for workspace_user_role_mapping in
@@ -168,7 +168,7 @@ def get_permission_list(user,
168168
role_permission_mapping_dict = group_by(role_permission_mapping_list, lambda item: item.role_id)
169169
workspace_user_role_mapping_list = get_default_workspace_user_role_mapping_list([user.role])
170170
workspace_user_role_mapping_dict = group_by(workspace_user_role_mapping_list,
171-
lambda item: item.role_id)
171+
lambda item: item.workspace_id)
172172
# 资源权限
173173
workspace_resource_permission_list = get_workspace_resource_permission_list(
174174
workspace_user_resource_permission_list,

apps/common/constants/permission_constants.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class Group(Enum):
2626

2727
TOOL = "TOOL"
2828

29+
WORKSPACE_USER_RESOURCE_PERMISSION = "WORKSPACE_USER_RESOURCE_PERMISSION"
30+
2931

3032
class Operate(Enum):
3133
"""
@@ -227,6 +229,11 @@ class PermissionConstants(Enum):
227229
DOCUMENT_DELETE = Permission(group=Group.KNOWLEDGE, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN,
228230
RoleConstants.USER])
229231

232+
WORKSPACE_USER_RESOURCE_PERMISSION_READ = Permission(group=Group.WORKSPACE_USER_RESOURCE_PERMISSION,
233+
operate=Operate.READ,
234+
role_list=[RoleConstants.ADMIN,
235+
RoleConstants.WORKSPACE_MANAGE])
236+
230237
def get_workspace_application_permission(self):
231238
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
232239
resource_path=

apps/maxkb/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
path("api/", include("models_provider.urls")),
2727
path("api/", include("folders.urls")),
2828
path("api/", include("knowledge.urls")),
29+
path("api/", include("system_manage.urls")),
2930
]
3031
urlpatterns += [
3132
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的

apps/system_manage/api/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: __init__.py
6+
@date:2025/4/28 17:05
7+
@desc:
8+
"""
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: workspace_user_resource_permission.py
6+
@date:2025/4/28 18:13
7+
@desc:
8+
"""
9+
from drf_spectacular.types import OpenApiTypes
10+
from drf_spectacular.utils import OpenApiParameter
11+
12+
from common.mixins.api_mixin import APIMixin
13+
from common.result import ResultSerializer
14+
from system_manage.serializers.user_resource_permission import UserResourcePermissionResponse, \
15+
UpdateUserResourcePermissionRequest
16+
17+
18+
class APIUserResourcePermissionResponse(ResultSerializer):
19+
def get_data(self):
20+
return UserResourcePermissionResponse(many=True)
21+
22+
23+
class UserResourcePermissionAPI(APIMixin):
24+
@staticmethod
25+
def get_parameters():
26+
return [
27+
OpenApiParameter(
28+
name="workspace_id",
29+
description="工作空间id",
30+
type=OpenApiTypes.STR,
31+
location='path',
32+
required=True,
33+
)
34+
]
35+
36+
@staticmethod
37+
def get_response():
38+
return APIUserResourcePermissionResponse
39+
40+
41+
class EditUserResourcePermissionAPI(APIMixin):
42+
@staticmethod
43+
def get_request():
44+
return UpdateUserResourcePermissionRequest()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: __init__.py
6+
@date:2025/4/28 17:05
7+
@desc:
8+
"""
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: workspace_user_resource_permission.py
6+
@date:2025/4/28 17:17
7+
@desc:
8+
"""
9+
import json
10+
import os
11+
12+
from django.core.cache import cache
13+
from django.db.models import QuerySet
14+
from django.utils.translation import gettext_lazy as _
15+
from rest_framework import serializers
16+
17+
from common.constants.cache_version import Cache_Version
18+
from common.constants.permission_constants import get_default_workspace_user_role_mapping_list, RoleConstants, \
19+
ResourcePermissionGroup, ResourcePermissionRole, ResourceAuthType
20+
from common.database_model_manage.database_model_manage import DatabaseModelManage
21+
from common.db.search import native_search
22+
from common.db.sql_execute import select_list
23+
from common.exception.app_exception import AppApiException
24+
from common.utils.common import get_file_content
25+
from common.utils.split_model import group_by
26+
from knowledge.models import Knowledge
27+
from maxkb.conf import PROJECT_DIR
28+
from system_manage.models import WorkspaceUserResourcePermission, AuthTargetType
29+
30+
31+
class PermissionSerializer(serializers.Serializer):
32+
VIEW = serializers.BooleanField(required=True, label="可读")
33+
MANAGE = serializers.BooleanField(required=True, label="管理")
34+
ROLE = serializers.BooleanField(required=True, label="跟随角色")
35+
36+
37+
class UserResourcePermissionItemResponse(serializers.Serializer):
38+
id = serializers.UUIDField(required=True, label="主键id")
39+
name = serializers.CharField(required=True, label="资源名称")
40+
auth_target_type = serializers.ChoiceField(required=True, choices=AuthTargetType.choices, label="授权资源")
41+
user_id = serializers.UUIDField(required=True, label="用户id")
42+
auth_type = serializers.ChoiceField(required=True, choices=ResourceAuthType.choices, label="授权类型")
43+
permission = PermissionSerializer()
44+
45+
46+
class UserResourcePermissionResponse(serializers.Serializer):
47+
KNOWLEDGE = UserResourcePermissionItemResponse(many=True)
48+
49+
50+
class UpdateTeamMemberItemPermissionSerializer(serializers.Serializer):
51+
auth_target_type = serializers.ChoiceField(required=True, choices=AuthTargetType.choices, label="授权资源")
52+
target_id = serializers.CharField(required=True, label=_('target id'))
53+
auth_type = serializers.ChoiceField(required=True, choices=ResourceAuthType.choices, label="授权类型")
54+
permission = PermissionSerializer(required=True, many=False)
55+
56+
57+
class UpdateUserResourcePermissionRequest(serializers.Serializer):
58+
user_resource_permission_list = UpdateTeamMemberItemPermissionSerializer(required=True, many=True)
59+
60+
def is_valid(self, *, workspace_id=None, raise_exception=False):
61+
super().is_valid(raise_exception=True)
62+
user_resource_permission_list = self.data.get("user_resource_permission_list")
63+
illegal_target_id_list = select_list(
64+
get_file_content(
65+
os.path.join(PROJECT_DIR, "apps", "system_manage", 'sql', 'check_member_permission_target_exists.sql')),
66+
[json.dumps(user_resource_permission_list), workspace_id])
67+
if illegal_target_id_list is not None and len(illegal_target_id_list) > 0:
68+
raise AppApiException(500,
69+
_('Non-existent application|knowledge base id[') + str(illegal_target_id_list) + ']')
70+
71+
72+
class UserResourcePermissionSerializer(serializers.Serializer):
73+
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
74+
75+
def get_queryset(self):
76+
return {
77+
"knowledge_query_set": QuerySet(Knowledge)
78+
.filter(workspace_id=self.data.get('workspace_id')),
79+
'workspace_user_resource_permission_query_set': QuerySet(WorkspaceUserResourcePermission).filter(
80+
workspace_id=self.data.get('workspace_id'))
81+
}
82+
83+
def list(self, user, with_valid=True):
84+
if with_valid:
85+
self.is_valid(raise_exception=True)
86+
workspace_id = self.data.get("workspace_id")
87+
# 用户权限列表
88+
user_resource_permission_list = native_search(self.get_queryset(), get_file_content(
89+
os.path.join(PROJECT_DIR, "apps", "system_manage", 'sql', 'get_user_resource_permission.sql')))
90+
workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
91+
workspace_model = DatabaseModelManage.get_model("workspace_model")
92+
if workspace_user_role_mapping_model and workspace_model:
93+
workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(user_id=user.id,
94+
workspace_id=workspace_id)
95+
else:
96+
workspace_user_role_mapping_list = get_default_workspace_user_role_mapping_list([user.role])
97+
is_workspace_manage = any(
98+
[workspace_user_role_mapping for workspace_user_role_mapping in workspace_user_role_mapping_list if
99+
workspace_user_role_mapping.role_id == RoleConstants.WORKSPACE_MANAGE.value])
100+
# 如果当前用户是当前工作空间管理员那么就拥有所有权限
101+
if is_workspace_manage:
102+
user_resource_permission_list = list(
103+
map(lambda row: {**row,
104+
'permission': {ResourcePermissionGroup.VIEW.value: True,
105+
ResourcePermissionGroup.MANAGE.value: True,
106+
ResourcePermissionRole.ROLE.value: True}},
107+
user_resource_permission_list))
108+
return group_by([{**user_resource_permission, 'permission': {
109+
permission: True if user_resource_permission.get('permission_list').__contains__(permission) else False for
110+
permission in
111+
[ResourcePermissionGroup.VIEW.value, ResourcePermissionGroup.MANAGE.value,
112+
ResourcePermissionRole.ROLE.value]}}
113+
for user_resource_permission in user_resource_permission_list],
114+
key=lambda item: item.get('auth_target_type'))
115+
116+
def edit(self, instance, user, with_valid=True):
117+
if with_valid:
118+
self.is_valid(raise_exception=True)
119+
UpdateUserResourcePermissionRequest(data=instance).is_valid(raise_exception=True,
120+
workspace_id=self.data.get('workspace_id'))
121+
workspace_id = self.data.get("workspace_id")
122+
update_list = []
123+
save_list = []
124+
user_resource_permission_list = instance.get('user_resource_permission_list')
125+
workspace_user_resource_permission_exist_list = QuerySet(WorkspaceUserResourcePermission).filter(
126+
workspace_id=workspace_id)
127+
for user_resource_permission in user_resource_permission_list:
128+
exist_list = [user_resource_permission_exist for user_resource_permission_exist in
129+
workspace_user_resource_permission_exist_list if
130+
user_resource_permission.get('target_id') == str(user_resource_permission_exist.target)]
131+
if len(exist_list) > 0:
132+
exist_list[0].permission_list = [key for key in user_resource_permission.get('permission').keys() if
133+
user_resource_permission.get('permission').get(key)]
134+
update_list.append(exist_list[0])
135+
else:
136+
save_list.append(WorkspaceUserResourcePermission(target=user_resource_permission.get('target_id'),
137+
auth_target_type=user_resource_permission.get(
138+
'auth_target_type'),
139+
permission_list=[key for key in
140+
user_resource_permission.get(
141+
'permission').keys() if
142+
user_resource_permission.get(
143+
'permission').get(key)],
144+
workspace_id=workspace_id,
145+
user_id=user.id,
146+
auth_type=user_resource_permission.get('auth_type')))
147+
# 批量更新
148+
QuerySet(WorkspaceUserResourcePermission).bulk_update(update_list, ['permission_list']) if len(
149+
update_list) > 0 else None
150+
# 批量插入
151+
QuerySet(WorkspaceUserResourcePermission).bulk_create(save_list) if len(save_list) > 0 else None
152+
version = Cache_Version.PERMISSION_LIST.get_version()
153+
key = Cache_Version.PERMISSION_LIST.get_key(user_id=str(user.id))
154+
cache.delete(key, version=version)
155+
return True
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
SELECT
2+
static_temp."target_id"::text
3+
FROM
4+
(SELECT * FROM json_to_recordset(
5+
%s
6+
) AS x(target_id uuid,auth_target_type text)) static_temp
7+
LEFT JOIN (
8+
SELECT
9+
"id",
10+
'KNOWLEDGE' AS "auth_target_type"
11+
FROM
12+
knowledge
13+
WHERE workspace_id= %s
14+
) "app_and_knowledge_temp"
15+
ON "app_and_knowledge_temp"."id" = static_temp."target_id" and app_and_knowledge_temp."auth_target_type"=static_temp."auth_target_type"
16+
WHERE app_and_knowledge_temp.id is NULL ;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
SELECT app_or_knowledge.*,
2+
COALESCE(workspace_user_resource_permission.permission_list,'{}')::varchar[] as permission_list,
3+
COALESCE(workspace_user_resource_permission.auth_type,'ROLE') as auth_type
4+
FROM (SELECT "id",
5+
"name",
6+
'KNOWLEDGE' AS "auth_target_type",
7+
user_id,
8+
workspace_id,
9+
"type" AS "icon"
10+
FROM knowledge
11+
${knowledge_query_set}
12+
) app_or_knowledge
13+
LEFT JOIN (SELECT *
14+
FROM workspace_user_resource_permission
15+
${workspace_user_resource_permission_query_set}) workspace_user_resource_permission
16+
ON workspace_user_resource_permission.target = app_or_knowledge."id";

apps/system_manage/urls.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from django.urls import path
2+
3+
from . import views
4+
5+
app_name = "system_manage"
6+
urlpatterns = [
7+
path('workspace/<str:workspace_id>/user_resource_permission', views.WorkSpaceUserResourcePermissionView.as_view())
8+
]

0 commit comments

Comments
 (0)