Skip to content

Commit e58a550

Browse files
committed
init project
1 parent 3502522 commit e58a550

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2717
-0
lines changed

apps/common/auth/__init__.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: __init__.py.py
6+
@date:2025/4/14 10:44
7+
@desc:
8+
"""
9+
from .authenticate import *

apps/common/auth/authenticate.py

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# coding=utf-8
2+
"""
3+
@project: qabot
4+
@Author:虎虎
5+
@file: authenticate.py
6+
@date:2023/9/4 11:16
7+
@desc: 认证类
8+
"""
9+
import traceback
10+
from importlib import import_module
11+
12+
from django.conf import settings
13+
from django.core import cache
14+
from django.core import signing
15+
from rest_framework.authentication import TokenAuthentication
16+
17+
from common.exception.app_exception import AppAuthenticationFailed, AppEmbedIdentityFailed, AppChatNumOutOfBoundsFailed, \
18+
ChatException, AppApiException
19+
from django.utils.translation import gettext_lazy as _
20+
21+
token_cache = cache.caches['default']
22+
23+
24+
class AnonymousAuthentication(TokenAuthentication):
25+
def authenticate(self, request):
26+
return None, None
27+
28+
29+
def new_instance_by_class_path(class_path: str):
30+
parts = class_path.rpartition('.')
31+
package_path = parts[0]
32+
class_name = parts[2]
33+
module = import_module(package_path)
34+
HandlerClass = getattr(module, class_name)
35+
return HandlerClass()
36+
37+
38+
handles = [new_instance_by_class_path(class_path) for class_path in settings.AUTH_HANDLES]
39+
40+
41+
class TokenDetails:
42+
token_details = None
43+
is_load = False
44+
45+
def __init__(self, token: str):
46+
self.token = token
47+
48+
def get_token_details(self):
49+
if self.token_details is None and not self.is_load:
50+
try:
51+
self.token_details = signing.loads(self.token)
52+
except Exception as e:
53+
self.is_load = True
54+
return self.token_details
55+
56+
57+
class OpenAIKeyAuth(TokenAuthentication):
58+
def authenticate(self, request):
59+
auth = request.META.get('HTTP_AUTHORIZATION')
60+
auth = auth.replace('Bearer ', '')
61+
# 未认证
62+
if auth is None:
63+
raise AppAuthenticationFailed(1003, _('Not logged in, please log in first'))
64+
try:
65+
token_details = TokenDetails(auth)
66+
for handle in handles:
67+
if handle.support(request, auth, token_details.get_token_details):
68+
return handle.handle(request, auth, token_details.get_token_details)
69+
raise AppAuthenticationFailed(1002, _('Authentication information is incorrect! illegal user'))
70+
except Exception as e:
71+
traceback.format_exc()
72+
if isinstance(e, AppEmbedIdentityFailed) or isinstance(e, AppChatNumOutOfBoundsFailed) or isinstance(e,
73+
AppApiException):
74+
raise e
75+
raise AppAuthenticationFailed(1002, _('Authentication information is incorrect! illegal user'))
76+
77+
78+
class TokenAuth(TokenAuthentication):
79+
# 重新 authenticate 方法,自定义认证规则
80+
def authenticate(self, request):
81+
auth = request.META.get('HTTP_AUTHORIZATION')
82+
# 未认证
83+
if auth is None:
84+
raise AppAuthenticationFailed(1003, _('Not logged in, please log in first'))
85+
try:
86+
token_details = TokenDetails(auth)
87+
for handle in handles:
88+
if handle.support(request, auth, token_details.get_token_details):
89+
return handle.handle(request, auth, token_details.get_token_details)
90+
raise AppAuthenticationFailed(1002, _('Authentication information is incorrect! illegal user'))
91+
except Exception as e:
92+
traceback.format_exc()
93+
if isinstance(e, AppEmbedIdentityFailed) or isinstance(e, AppChatNumOutOfBoundsFailed) or isinstance(e,
94+
AppApiException):
95+
raise e
96+
raise AppAuthenticationFailed(1002, _('Authentication information is incorrect! illegal user'))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# coding=utf-8
2+
"""
3+
@project: qabot
4+
@Author:虎虎
5+
@file: authenticate.py
6+
@date:2024/3/14 03:02
7+
@desc: 认证处理器
8+
"""
9+
from abc import ABC, abstractmethod
10+
11+
12+
class AuthBaseHandle(ABC):
13+
@abstractmethod
14+
def support(self, request, token: str, get_token_details):
15+
pass
16+
17+
@abstractmethod
18+
def handle(self, request, token: str, get_token_details):
19+
pass
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# coding=utf-8
2+
"""
3+
@project: maxkb
4+
@Author:虎虎
5+
@file: authenticate.py
6+
@date:2024/3/14 03:02
7+
@desc: 用户认证
8+
"""
9+
from django.db.models import QuerySet
10+
from common.auth.handle.auth_base_handle import AuthBaseHandle
11+
from common.constants.authentication_type import AuthenticationType
12+
from common.constants.cache_version import Cache_Version
13+
from common.constants.permission_constants import Auth, RoleConstants
14+
from common.exception.app_exception import AppAuthenticationFailed
15+
from users.models import User
16+
from django.core.cache import cache
17+
from django.utils.translation import gettext_lazy as _
18+
19+
20+
class UserToken(AuthBaseHandle):
21+
def support(self, request, token: str, get_token_details):
22+
auth_details = get_token_details()
23+
if auth_details is None:
24+
return False
25+
return True
26+
27+
def handle(self, request, token: str, get_token_details):
28+
cache_token = cache.get(token, version=Cache_Version.TOKEN)
29+
if cache_token is None:
30+
raise AppAuthenticationFailed(1002, _('Login expired'))
31+
auth_details = get_token_details()
32+
user = QuerySet(User).get(id=auth_details['id'])
33+
role = RoleConstants[user.role]
34+
return user, Auth([], [],
35+
client_id=str(user.id),
36+
client_type=AuthenticationType.SYSTEM_USER.value, current_role=role)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# coding=utf-8
2+
"""
3+
@project: maxkb
4+
@Author:虎虎
5+
@file: authentication_type.py
6+
@date:2023/11/14 20:03
7+
@desc:
8+
"""
9+
from enum import Enum
10+
11+
12+
class AuthenticationType(Enum):
13+
# 系统用户
14+
SYSTEM_USER = "SYSTEM_USER"
15+
# 对话用户
16+
CHAT_USER = "CHAT_USER"
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: cache_version.py
6+
@date:2025/4/14 19:09
7+
@desc:
8+
"""
9+
from enum import Enum
10+
11+
12+
class Cache_Version(Enum):
13+
# 系统用户
14+
TOKEN = "TOKEN"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"""
2+
@project: qabot
3+
@Author:虎虎
4+
@file: permission_constants.py
5+
@date:2023/9/13 18:23
6+
@desc: 权限,角色 常量
7+
"""
8+
from enum import Enum
9+
from typing import List
10+
11+
from django.utils.translation import gettext_lazy as _
12+
13+
14+
class Group(Enum):
15+
"""
16+
权限组 一个组一般对应前端一个菜单
17+
"""
18+
USER = "USER"
19+
20+
21+
class Operate(Enum):
22+
"""
23+
一个权限组的操作权限
24+
"""
25+
READ = 'READ'
26+
EDIT = "EDIT"
27+
CREATE = "CREATE"
28+
DELETE = "DELETE"
29+
30+
31+
class RoleGroup(Enum):
32+
# 系统用户
33+
SYSTEM_USER = "SYSTEM_USER"
34+
# 对话用户
35+
CHAT_USER = "CHAT_USER"
36+
37+
38+
class Role:
39+
def __init__(self, name: str, decs: str, group: RoleGroup):
40+
self.name = name
41+
self.decs = decs
42+
self.group = group
43+
44+
45+
class RoleConstants(Enum):
46+
ADMIN = Role(_("ADMIN"), _('Super administrator'), RoleGroup.SYSTEM_USER)
47+
48+
49+
class Permission:
50+
"""
51+
权限信息
52+
"""
53+
54+
def __init__(self, group: Group, operate: Operate, roles=None, dynamic_tag=None):
55+
if roles is None:
56+
roles = []
57+
self.group = group
58+
self.operate = operate
59+
self.roleList = roles
60+
self.dynamic_tag = dynamic_tag
61+
62+
def __str__(self):
63+
return self.group.value + ":" + self.operate.value + (
64+
(":" + self.dynamic_tag) if self.dynamic_tag is not None else '')
65+
66+
def __eq__(self, other):
67+
return str(self) == str(other)
68+
69+
70+
class PermissionConstants(Enum):
71+
"""
72+
权限枚举
73+
"""
74+
USER_READ = Permission(group=Group.USER, operate=Operate.READ, roles=[RoleConstants.ADMIN])
75+
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, roles=[RoleConstants.ADMIN])
76+
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, roles=[RoleConstants.ADMIN])
77+
78+
79+
def get_permission_list_by_role(role: RoleConstants):
80+
"""
81+
根据角色 获取角色对应的权限
82+
:param role: 角色
83+
:return: 权限
84+
"""
85+
return list(map(lambda k: PermissionConstants[k],
86+
list(filter(lambda k: PermissionConstants[k].value.roleList.__contains__(role),
87+
PermissionConstants.__members__))))
88+
89+
90+
class Auth:
91+
"""
92+
用于存储当前用户的角色和权限
93+
"""
94+
95+
def __init__(self, role_list: List[RoleConstants], permission_list: List[PermissionConstants | Permission]
96+
, client_id, client_type, current_role: RoleConstants, **keywords):
97+
self.role_list = role_list
98+
self.permission_list = permission_list
99+
self.client_id = client_id
100+
self.client_type = client_type
101+
self.keywords = keywords
102+
self.current_role = current_role
103+
104+
105+
class CompareConstants(Enum):
106+
# 或者
107+
OR = "OR"
108+
# 并且
109+
AND = "AND"
110+
111+
112+
class ViewPermission:
113+
def __init__(self, roleList: List[RoleConstants], permissionList: List[PermissionConstants | object],
114+
compare=CompareConstants.OR):
115+
self.roleList = roleList
116+
self.permissionList = permissionList
117+
self.compare = compare
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# coding=utf-8
2+
"""
3+
@project: qabot
4+
@Author:虎虎
5+
@file: app_exception.py
6+
@date:2023/9/4 14:04
7+
@desc:
8+
"""
9+
from rest_framework import status
10+
11+
12+
class AppApiException(Exception):
13+
"""
14+
项目内异常
15+
"""
16+
status_code = status.HTTP_200_OK
17+
18+
def __init__(self, code, message):
19+
self.code = code
20+
self.message = message
21+
22+
23+
class NotFound404(AppApiException):
24+
"""
25+
未认证(未登录)异常
26+
"""
27+
status_code = status.HTTP_404_NOT_FOUND
28+
29+
def __init__(self, code, message):
30+
self.code = code
31+
self.message = message
32+
33+
34+
class AppAuthenticationFailed(AppApiException):
35+
"""
36+
未认证(未登录)异常
37+
"""
38+
status_code = status.HTTP_401_UNAUTHORIZED
39+
40+
def __init__(self, code, message):
41+
self.code = code
42+
self.message = message
43+
44+
45+
class AppUnauthorizedFailed(AppApiException):
46+
"""
47+
未授权(没有权限)异常
48+
"""
49+
status_code = status.HTTP_403_FORBIDDEN
50+
51+
def __init__(self, code, message):
52+
self.code = code
53+
self.message = message
54+
55+
56+
class AppEmbedIdentityFailed(AppApiException):
57+
"""
58+
嵌入cookie异常
59+
"""
60+
status_code = 460
61+
62+
def __init__(self, code, message):
63+
self.code = code
64+
self.message = message
65+
66+
67+
class AppChatNumOutOfBoundsFailed(AppApiException):
68+
"""
69+
访问次数超过今日访问量
70+
"""
71+
status_code = 461
72+
73+
def __init__(self, code, message):
74+
self.code = code
75+
self.message = message
76+
77+
78+
class ChatException(AppApiException):
79+
status_code = 500
80+
81+
def __init__(self, code, message):
82+
self.code = code
83+
self.message = message

0 commit comments

Comments
 (0)