Skip to content

Commit 5f902ef

Browse files
authored
feat: authentication (#2906)
1 parent 475a05e commit 5f902ef

File tree

15 files changed

+268
-62
lines changed

15 files changed

+268
-62
lines changed

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

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,56 @@
66
@date:2024/3/14 03:02
77
@desc: 用户认证
88
"""
9+
import datetime
10+
from functools import reduce
11+
912
from django.core.cache import cache
1013
from django.db.models import QuerySet
1114
from django.utils.translation import gettext_lazy as _
1215

1316
from common.auth.handle.auth_base_handle import AuthBaseHandle
1417
from common.constants.cache_version import Cache_Version
15-
from common.constants.permission_constants import Auth, RoleConstants, get_default_permission_list_by_role
18+
from common.constants.permission_constants import Auth, RoleConstants, get_default_permission_list_by_role, \
19+
PermissionConstants
1620
from common.database_model_manage.database_model_manage import DatabaseModelManage
1721
from common.exception.app_exception import AppAuthenticationFailed
22+
from common.utils.common import group_by
23+
from system_manage.models.workspace_user_permission import WorkspaceUserPermission
1824
from users.models import User
1925

2026

21-
def get_permission_list(user_id,
22-
workspace_id,
27+
def get_permission(permission_id):
28+
if isinstance(permission_id, PermissionConstants):
29+
permission_id = permission_id.value
30+
return f"{permission_id}"
31+
32+
33+
def get_workspace_permission(permission_id, workspace_id):
34+
if isinstance(permission_id, PermissionConstants):
35+
permission_id = permission_id.value
36+
return f"{permission_id}:/WORKSPACE/{workspace_id}"
37+
38+
39+
def get_workspace_resource_permission_list(permission_id, workspace_id, workspace_user_permission_dict):
40+
workspace_user_permission_list = workspace_user_permission_dict.get(workspace_id)
41+
if workspace_user_permission_list is None:
42+
return [
43+
get_workspace_permission(permission_id, workspace_id), get_permission(permission_id)]
44+
return [
45+
f"{permission_id}:/WORKSPACE/{workspace_id}/{workspace_user_permission.auth_target_type}/{workspace_user_permission.target}"
46+
for workspace_user_permission in
47+
workspace_user_permission_list if workspace_user_permission.is_auth] + [
48+
get_workspace_permission(permission_id, workspace_id), get_permission(permission_id)]
49+
50+
51+
def get_permission_list(user,
2352
workspace_user_role_mapping_model,
2453
workspace_model,
2554
role_model,
2655
role_permission_mapping_model):
27-
version, get_key = Cache_Version.PERMISSION_LIST.value
28-
key = get_key(user_id, workspace_id)
56+
user_id = user.id
57+
version = Cache_Version.PERMISSION_LIST.get_version()
58+
key = Cache_Version.PERMISSION_LIST.get_key(user_id=user_id)
2959
# 获取权限列表
3060
is_query_model = workspace_user_role_mapping_model is not None and workspace_model is not None and role_model is not None and role_permission_mapping_model is not None
3161
permission_list = cache.get(key, version=version)
@@ -37,71 +67,80 @@ def get_permission_list(user_id,
3767
role_permission_mapping_list = QuerySet(role_permission_mapping_model).filter(
3868
role_id__in=[workspace_user_role_mapping.role_id for workspace_user_role_mapping in
3969
workspace_user_role_mapping_list])
40-
permission_list = [role_model.id for role_model in role_permission_mapping_list]
70+
role_dict = group_by(role_permission_mapping_list, lambda item: item.get('role_id'))
71+
72+
workspace_user_permission_list = QuerySet(WorkspaceUserPermission).filter(
73+
workspace_id__in=[workspace_user_role.workspace_id for workspace_user_role in
74+
workspace_user_role_mapping_list])
75+
workspace_user_permission_dict = group_by(workspace_user_permission_list,
76+
key=lambda item: item.workspace_id)
77+
permission_list = [
78+
get_workspace_resource_permission_list(role_permission_mapping.permission_id,
79+
role_dict.get(role_permission_mapping.role_id).workspace_id,
80+
workspace_user_permission_dict)
81+
for role_permission_mapping in
82+
role_permission_mapping_list]
83+
84+
# 将二维数组扁平为一维
85+
permission_list = reduce(lambda x, y: [*x, *y], permission_list, [])
4186
cache.set(key, permission_list, version=version)
4287
else:
43-
permission_list = get_default_permission_list_by_role(RoleConstants.ADMIN)
88+
workspace_id_list = ['default']
89+
workspace_user_permission_list = QuerySet(WorkspaceUserPermission).filter(
90+
workspace_id__in=workspace_id_list)
91+
92+
workspace_user_permission_dict = group_by(workspace_user_permission_list,
93+
key=lambda item: item.workspace_id)
94+
permission_list = get_default_permission_list_by_role(RoleConstants[user.role])
95+
permission_list = [
96+
get_workspace_resource_permission_list(permission, 'default', workspace_user_permission_dict) for
97+
permission
98+
in permission_list]
99+
# 将二维数组扁平为一维
100+
permission_list = reduce(lambda x, y: [*x, *y], permission_list, [])
44101
cache.set(key, permission_list, version=version)
45102
return permission_list
46103

47104

48-
def get_workspace_list(user_id,
49-
workspace_id,
50-
workspace_user_role_mapping_model,
51-
workspace_model,
52-
role_model,
53-
role_permission_mapping_model):
54-
version, get_key = Cache_Version.WORKSPACE_LIST.value
55-
key = get_key(user_id)
56-
workspace_list = cache.get(key, version=version)
57-
# 获取权限列表
58-
is_query_model = workspace_user_role_mapping_model is not None and workspace_model is not None and role_model is not None and role_permission_mapping_model is not None
59-
if workspace_list is None:
60-
if is_query_model:
61-
# 获取工作空间 用户 角色映射数据
62-
workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(user_id=user_id)
63-
cache.set(key, [workspace_user_role_mapping.workspace_id for workspace_user_role_mapping in
64-
workspace_user_role_mapping_list], version=version)
65-
else:
66-
return ["default"]
67-
return workspace_list
68-
69-
70105
def get_role_list(user,
71-
workspace_id,
72106
workspace_user_role_mapping_model,
73107
workspace_model,
74108
role_model,
75109
role_permission_mapping_model):
76-
version, get_key = Cache_Version.ROLE_LIST.value
77-
key = get_key(user.id, workspace_id)
110+
"""
111+
获取当前用户的角色列表
112+
"""
113+
version = Cache_Version.ROLE_LIST.get_version()
114+
key = Cache_Version.ROLE_LIST.get_key(user_id=user.id)
78115
workspace_list = cache.get(key, version=version)
79116
# 获取权限列表
80117
is_query_model = workspace_user_role_mapping_model is not None and workspace_model is not None and role_model is not None and role_permission_mapping_model is not None
81118
if workspace_list is None:
82119
if is_query_model:
83120
# 获取工作空间 用户 角色映射数据
84121
workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(user_id=user.id)
85-
cache.set(key, [workspace_user_role_mapping.role_id for workspace_user_role_mapping in
86-
workspace_user_role_mapping_list], version=version)
122+
cache.set(key,
123+
[f"{workspace_user_role_mapping.role_id}:/WORKSPACE/{workspace_user_role_mapping.workspace_id}"
124+
for
125+
workspace_user_role_mapping in
126+
workspace_user_role_mapping_list] + [user.role], version=version)
87127
else:
88128
cache.set(key, [user.role], version=version)
89129
return [user.role]
90130
return workspace_list
91131

92132

93-
def get_auth(user, workspace_id):
133+
def get_auth(user):
94134
workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
95135
workspace_model = DatabaseModelManage.get_model("workspace_model")
96136
role_model = DatabaseModelManage.get_model("role_model")
97137
role_permission_mapping_model = DatabaseModelManage.get_model("role_permission_mapping_model")
98-
workspace_list = get_workspace_list(user.id, workspace_id, workspace_user_role_mapping_model, workspace_model,
99-
role_model, role_permission_mapping_model)
100-
permission_list = get_permission_list(user.id, workspace_id, workspace_user_role_mapping_model, workspace_model,
138+
139+
permission_list = get_permission_list(user, workspace_user_role_mapping_model, workspace_model,
101140
role_model, role_permission_mapping_model)
102-
role_list = get_role_list(user, workspace_id, workspace_user_role_mapping_model, workspace_model,
141+
role_list = get_role_list(user, workspace_user_role_mapping_model, workspace_model,
103142
role_model, role_permission_mapping_model)
104-
return Auth(workspace_list, workspace_id, role_list, permission_list)
143+
return Auth(role_list, permission_list)
105144

106145

107146
class UserToken(AuthBaseHandle):
@@ -117,8 +156,7 @@ def handle(self, request, token: str, get_token_details):
117156
if cache_token is None:
118157
raise AppAuthenticationFailed(1002, _('Login expired'))
119158
auth_details = get_token_details()
120-
# 当前工作空间
121-
current_workspace = auth_details['current_workspace']
159+
cache.touch(token, timeout=datetime.timedelta(seconds=60 * 60 * 2).seconds, version=version)
122160
user = QuerySet(User).get(id=auth_details['id'])
123-
auth = get_auth(user, current_workspace)
161+
auth = get_auth(user)
124162
return user, auth

apps/common/constants/cache_version.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@ class Cache_Version(Enum):
1616
WORKSPACE_LIST = "WORKSPACE::LIST", lambda user_id: user_id
1717
# 用户数据
1818
USER = "USER", lambda user_id: user_id
19-
# 当前用户在当前工作空间的角色列表+本身的角色
20-
ROLE_LIST = "ROLE::LIST", lambda user_id, workspace_id: f"{user_id}::{workspace_id}"
21-
# 当前用户在当前工作空间的权限列表+本身的权限列表
22-
PERMISSION_LIST = "PERMISSION::LIST", lambda user_id, workspace_id: f"{user_id}::{workspace_id}"
19+
# 当前用户所有的角色
20+
ROLE_LIST = "ROLE::LIST", lambda user_id: user_id
21+
# 当前用户所有权限
22+
PERMISSION_LIST = "PERMISSION::LIST", lambda user_id: user_id
2323

24+
def get_version(self):
25+
return self.value[0]
2426

25-
version, get_key = Cache_Version.TOKEN.value
27+
def get_key_func(self):
28+
return self.value[1]
29+
30+
def get_key(self, **kwargs):
31+
return self.value[1](**kwargs)

apps/common/constants/permission_constants.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class Group(Enum):
1515
"""
1616
USER = "USER"
1717

18+
APPLICATION = "APPLICATION"
19+
20+
KNOWLEDGE = "KNOWLEDGE"
21+
1822

1923
class Operate(Enum):
2024
"""
@@ -38,29 +42,44 @@ class RoleGroup(Enum):
3842

3943

4044
class Role:
41-
def __init__(self, name: str, decs: str, group: RoleGroup):
45+
def __init__(self, name: str, decs: str, group: RoleGroup, resource_path=None):
4246
self.name = name
4347
self.decs = decs
4448
self.group = group
49+
self.resource_path = resource_path
50+
51+
def __str__(self):
52+
return self.name + (
53+
(":" + self.resource_path) if self.resource_path is not None else '')
54+
55+
def __eq__(self, other):
56+
return str(self) == str(other)
4557

4658

4759
class RoleConstants(Enum):
4860
ADMIN = Role("ADMIN", '超级管理员', RoleGroup.SYSTEM_USER)
4961
WORKSPACE_MANAGE = Role("WORKSPACE_MANAGE", '工作空间管理员', RoleGroup.SYSTEM_USER)
5062
USER = Role("USER", '普通用户', RoleGroup.SYSTEM_USER)
5163

64+
def get_workspace_role(self):
65+
return lambda r, kwargs: Role(name=self.value.name,
66+
decs=self.value.decs,
67+
group=self.value.group,
68+
resource_path=
69+
f"/WORKSPACE/{kwargs.get('workspace_id')}")
70+
5271

5372
class Permission:
5473
"""
5574
权限信息
5675
"""
5776

58-
def __init__(self, group: Group, operate: Operate, dynamic_tag=None, role_list=None):
77+
def __init__(self, group: Group, operate: Operate, resource_path=None, role_list=None):
5978
if role_list is None:
6079
role_list = []
6180
self.group = group
6281
self.operate = operate
63-
self.dynamic_tag = dynamic_tag
82+
self.resource_path = resource_path
6483
# 用于获取角色与权限的关系,只适用于没有权限管理的
6584
self.role_list = role_list
6685

@@ -76,7 +95,7 @@ def new_instance(permission_str: str):
7695

7796
def __str__(self):
7897
return self.group.value + ":" + self.operate.value + (
79-
(":" + self.dynamic_tag) if self.dynamic_tag is not None else '')
98+
(":" + self.resource_path) if self.resource_path is not None else '')
8099

81100
def __eq__(self, other):
82101
return str(self) == str(other)
@@ -91,6 +110,27 @@ class PermissionConstants(Enum):
91110
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN])
92111
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN])
93112

113+
def get_workspace_application_permission(self):
114+
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
115+
resource_path=
116+
f"/WORKSPACE/{kwargs.get('workspace_id')}/APPLICATION/{kwargs.get('application_id')}")
117+
118+
def get_workspace_knowledge_permission(self):
119+
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
120+
resource_path=
121+
f"/WORKSPACE/{kwargs.get('workspace_id')}/KNOWLEDGE/{kwargs.get('knowledge_id')}")
122+
123+
def get_workspace_permission(self):
124+
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
125+
resource_path=
126+
f"/WORKSPACE/{kwargs.get('workspace_id')}")
127+
128+
def __eq__(self, other):
129+
if isinstance(other, PermissionConstants):
130+
return other == self
131+
else:
132+
return self.value == other
133+
94134

95135
def get_default_permission_list_by_role(role: RoleConstants):
96136
"""
@@ -109,15 +149,9 @@ class Auth:
109149
"""
110150

111151
def __init__(self,
112-
work_space_list: List,
113-
current_workspace,
114152
current_role_list: List[Role],
115153
permission_list: List[PermissionConstants | Permission],
116154
**keywords):
117-
# 当前用户所有工作空间
118-
self.work_space_list = work_space_list
119-
# 当前工作空间
120-
self.current_workspace = current_workspace
121155
# 当前工作空间的所有权限+非工作空间权限
122156
self.permission_list = permission_list
123157
# 当前工作空间角色列表

apps/common/utils/common.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@desc:
88
"""
99
import hashlib
10+
from typing import List
1011

1112

1213
def password_encrypt(row_password):
@@ -19,3 +20,19 @@ def password_encrypt(row_password):
1920
md5.update(row_password.encode()) # 3,对字符串的字节类型加密
2021
result = md5.hexdigest() # 4,加密
2122
return result
23+
24+
25+
def group_by(list_source: List, key):
26+
"""
27+
將數組分組
28+
:param list_source: 需要分組的數組
29+
:param key: 分組函數
30+
:return: key->[]
31+
"""
32+
result = {}
33+
for e in list_source:
34+
k = key(e)
35+
array = result.get(k) if k in result else []
36+
array.append(e)
37+
result[k] = array
38+
return result

apps/maxkb/settings/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
'drf_spectacular',
4040
'drf_spectacular_sidecar',
4141
'users.apps.UsersConfig',
42-
'common'
42+
'common',
43+
'system_manage'
4344
]
4445

4546
MIDDLEWARE = [

apps/system_manage/__init__.py

Whitespace-only changes.

apps/system_manage/admin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.contrib import admin
2+
3+
# Register your models here.

apps/system_manage/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class SystemManageConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
6+
name = 'system_manage'

0 commit comments

Comments
 (0)