Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/chat/api/chat_authentication_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def get_request():
class ChatAuthenticationAPI(APIMixin):
@staticmethod
def get_request():
return AnonymousAuthenticationSerializer
return None

@staticmethod
def get_parameters():
Expand Down
29 changes: 9 additions & 20 deletions apps/chat/serializers/chat_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers

from application.models import ApplicationAccessToken, ChatUserType, Application, ApplicationVersion
from application.models import ApplicationAccessToken, Application, ApplicationVersion
from application.serializers.application import ApplicationSerializerModel
from common.auth.common import ChatUserToken, ChatAuthentication, FileToken
from common.auth.common import FileToken, ChatToken
from common.auth.constants.operate_constants import Operate
from common.constants.authentication_type import AuthenticationType
from common.constants.cache_version import Cache_Version
from common.database_model_manage.database_model_manage import DatabaseModelManage
Expand All @@ -24,9 +25,7 @@


class AnonymousAuthenticationSerializer(serializers.Serializer):
access_token = serializers.CharField(required=True, label=_("access_token"))

def auth(self, request, with_valid=True):
def auth(self, request):
token = request.META.get('HTTP_AUTHORIZATION')
token_details = {}
try:
Expand All @@ -35,21 +34,11 @@ def auth(self, request, with_valid=True):
token_details = signing.loads(token[7:])
except Exception as e:
pass
if with_valid:
self.is_valid(raise_exception=True)
access_token = self.data.get("access_token")
application_access_token = QuerySet(ApplicationAccessToken).filter(access_token=access_token).first()
if application_access_token is not None and application_access_token.is_active:
chat_user_id = token_details.get('chat_user_id') or str(uuid.uuid7())
_type = AuthenticationType.CHAT_ANONYMOUS_USER
return ChatUserToken(application_access_token.application_id, None, access_token, _type,
ChatUserType.ANONYMOUS_USER,
chat_user_id, ChatAuthentication(None)).to_token(), FileToken(chat_user_id,
AuthenticationType.CHAT_ANONYMOUS_USER.value,
application_id=str(
application_access_token.application_id)).to_token()
else:
raise NotFound404(404, _("Invalid access_token"))
chat_user_id = token_details.get('id') or str(uuid.uuid7())
_type = AuthenticationType.CHAT_USER
return ChatToken(chat_user_id, _type,
str(Operate.ANNOTATION_AUTH)).to_token(), FileToken(chat_user_id,
_type).to_token()


class AuthProfileSerializer(serializers.Serializer):
Expand Down
5 changes: 1 addition & 4 deletions apps/chat/views/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ def options(self, request, *args, **kwargs):
tags=[_('Chat')] # type: ignore
)
def post(self, request: Request):
token, f_token = AnonymousAuthenticationSerializer(
data={'access_token': request.data.get("access_token")}).auth(
token, f_token = AnonymousAuthenticationSerializer().auth(
request)
response = result.success(
token,
Expand Down Expand Up @@ -369,7 +368,6 @@ def post(self, request: Request):
return result.error(_("Failed to change password"))



class ChatUserProfileView(APIView):
authentication_classes = [ChatTokenAuth]

Expand Down Expand Up @@ -444,4 +442,3 @@ def post(self, request: Request):
auth = request.META.get("HTTP_AUTHORIZATION")
cache.delete(get_key(token=auth[7:]), version=version)
return result.success(True)

21 changes: 20 additions & 1 deletion apps/common/auth/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

from django.core import signing, cache

from application.models import ChatUserType
from common.constants.authentication_type import AuthenticationType
from common.constants.cache_version import Cache_Version
from common.utils.rsa_util import encrypt, decrypt

Expand Down Expand Up @@ -67,7 +69,7 @@ def to_dict(self):
'application_id': self.application_id
} if self.application_id else {
'user_id': self.user_id,
'type': self.type
'type': str(self.type)
})

def to_token(self):
Expand Down Expand Up @@ -110,3 +112,20 @@ def new_instance(token_dict):
token_dict.get('access_token'), token_dict.get('type'), token_dict.get('chat_user_type'),
token_dict.get('chat_user_id'),
ChatAuthentication.new_instance(token_dict.get('authentication')))


class ChatToken:
def __init__(self, user_id, _type: AuthenticationType, login_type: str):
self.user_id = user_id
self.type = _type
self.login_type = login_type

def to_dict(self):
return {
'user_id': str(self.user_id),
'type': str(self.type.value),
'login_type': str(self.login_type)
}

def to_token(self):
return signing.dumps(self.to_dict())
40 changes: 40 additions & 0 deletions apps/common/auth/constants/chat_permission_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎虎
@file: chat_permission_constants.py
@date:2026/8/6 16:38
@desc:
"""
from enum import Enum

from common.auth.constants.group_constants import Group
from common.auth.constants.operate_constants import Operate
from common.auth.struct.permission import Permission


class ChatPermissionConstants(Enum):
CHAT_USER_ANONYMOUS = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.ANNOTATION_AUTH, 0)
CHAT_USER_PASSWORD = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.PASSWORD, 1)
CHAT_USER_LOCAL = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.LOCAL, 2)
CHAT_USER_CAS = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.CAS, 3)
CHAT_USER_DINGTALK = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.DINGTALK, 4)
CHAT_USER_WECOM = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.WECOM, 5)
CHAT_USER_LARK = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.LARK, 6)
CHAT_USER_OIDC = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.OIDC, 7)
CHAT_USER_LDAP = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.LDAP, 8)
CHAT_USER_OAUTH2 = Permission(Group.CHAT_USER, Group.CHAT_USER, Operate.OAUTH2, 9)

def get_permission(self):
return self._build_workspace_permission('application_id')

def _build_workspace_permission(self, resource_id_key=None):
def permission_factory(_, kwargs):
return Permission(group=self.value.group,
sub_group=self.value.sub_group,
operate=self.value.operate,
bit_index=self.value.bit_index,
workspace_id=kwargs.get('workspace_id'),
resource_id=kwargs.get(resource_id_key) if resource_id_key else None)

return permission_factory
1 change: 1 addition & 0 deletions apps/common/auth/constants/group_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class Group(Enum):
SYSTEM_OVERVIEW = ("SYSTEM_OVERVIEW", _("Overview"))
SYSTEM_ACCESS = ("SYSTEM_ACCESS", _("Application Access"))
SYSTEM_CHAT_LOG = ("SYSTEM_CHAT_LOG", _("Conversation log"))
CHAT = ("CHAT", _("Chat"))

def __init__(self, value, label):
self._value_ = value
Expand Down
14 changes: 14 additions & 0 deletions apps/common/auth/constants/operate_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ class Operate(Enum):
SYSTEM_API_KEY = ("READ+DELETE", _("System API Key"))
PORTAL = ("READ+PORTAL", _("Portal"))

ANNOTATION_AUTH = ('ANNOTATION', _("Annotation"))
PASSWORD = ("PASSWORD", _("Password verification"))
LOCAL = ("LOCAL", _("Account login"))
CAS = ("CAS", _("CAS"))
DINGTALK = ("DINGTALK", _("dingtalk"))
WECOM = ("WECOM", _("WeCom"))
LARK = ("LARK", _("lark"))
OIDC = ("OIDC", _("OIDC"))
LDAP = ("LDAP", _("LDAP"))
OAUTH2 = ("OAUTH2", _("OAUTH2"))

def __init__(self, value, label):
self._value_ = value
self.label = label

def __str__(self):
return self.value
49 changes: 0 additions & 49 deletions apps/common/auth/handle/impl/chat_anonymous_user_token.py

This file was deleted.

77 changes: 77 additions & 0 deletions apps/common/auth/handle/impl/chat_user_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: chat_anonymous_user_token.py
@date:2025/6/6 15:08
@desc:
"""
from functools import reduce

from django.db.models import QuerySet, Q

from application.models import ApplicationAccessToken, ChatUserType
from common.auth.constants.chat_permission_constants import ChatPermissionConstants
from common.auth.constants.group_constants import Group
from common.auth.constants.operate_constants import Operate
from common.auth.constants.permission_constants import PERMISSION_STR_MAP
from common.auth.handle.auth_base_handle import AuthBaseHandle
from common.auth.struct.auth import Principal, Auth
from common.constants.authentication_type import AuthenticationType
from system_manage.models import ResourceChatUserGroupAuthorize, ResourceType, ResourceChatUserAuthorize


class ChatUserToken(AuthBaseHandle):
def support(self, request, token: str, get_token_details):
token_details = get_token_details()
if token_details is None:
return False
return token_details.get('type') == AuthenticationType.CHAT_USER.value

def handle(self, request, token: str, get_token_details):
auth_details = get_token_details()
application_access_token_list = QuerySet(ApplicationAccessToken).filter(
is_active=True
)
_type = ChatUserType.ANONYMOUS_USER
login_type = auth_details.get('login_type')
if login_type.upper() == str(Operate.ANNOTATION_AUTH):
application_access_token_list = application_access_token_list.filter(authentication=False)
elif login_type.upper() == str(Operate.PASSWORD):
application_access_token_list = (application_access_token_list
.filter(authentication=True, authentication_value__type='password'))
else:
_type = ChatUserType.CHAT_USER
group_qs = QuerySet(ResourceChatUserGroupAuthorize).filter(
resource_type=ResourceType.APPLICATION,
is_auth=True,
).values_list('resource_id', flat=True)

user_qs = QuerySet(ResourceChatUserAuthorize).filter(
resource_type=ResourceType.APPLICATION,
is_auth=True,
).values_list('resource_id', flat=True)
application_access_token_list = application_access_token_list.filter(
Q(authentication_value__type='login'),
Q(authentication_value__login_value__contains=login_type),
Q(application_id__in=group_qs) | Q(application_id__in=user_qs),
)
permissions = {}
for application_access_token in application_access_token_list:
permission_list = []
if application_access_token.authentication:
if application_access_token.get('type') == 'password':
permission_list.append(ChatPermissionConstants.CHAT_USER_PASSWORD.value)
elif application_access_token.get('type') == 'login':
login_value = application_access_token.get('login_value') or []
for _value in login_value:
permission_str = f'{Group.CHAT_USER}_{_value.upper()}'
permission = PERMISSION_STR_MAP.get(permission_str)
if permission:
permission_list.append(permission)

else:
permission_list.append(ChatPermissionConstants.CHAT_USER_ANONYMOUS.value)
k = f"{Group.CHAT_USER}:r:{application_access_token.application_id}"
permissions[k] = reduce(lambda x, y: x | y, [p.value.bit() for p in permission_list], 0)
return Principal(auth_details.get('user_id'), _type), Auth(set(), permissions)
8 changes: 6 additions & 2 deletions apps/common/auth/struct/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ def __init__(self,


class Principal:
def __init__(self, _id, _type: UserType | ChatUserType, profile=None):
def __init__(self, _id,
_type: ChatUserType | UserType,
profile=None,
**keywords):
self.id = _id
self.type = _type
self._type = _type
self.profile = profile
self.keywords = keywords
8 changes: 5 additions & 3 deletions apps/common/auth/struct/permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class Permission:
"""
权限信息
"""
group: Group
sub_group: Group
group: Group | str
sub_group: Group | str
operate: Operate
bit_index: int
workspace_id: Optional[str] = None
Expand All @@ -33,7 +33,9 @@ def bit(self):
return 1 << self.bit_index

def get_resource_permission_key(self, resource_id):
return f"{self.group}:{resource_id}"
workspace = f"w:{self.workspace_id}" if self.workspace_id else ""
resource = f"r:{self.resource_id}" if self.resource_id else ""
return f"{self.group}{workspace}{resource}"

def __str__(self):
sub = f"_{self.sub_group}" if self.sub_group != self.group else ""
Expand Down
2 changes: 1 addition & 1 deletion apps/maxkb/settings/auth/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@desc:
"""
USER_TOKEN_AUTH = 'common.auth.handle.impl.user_token.UserToken'
CHAT_ANONYMOUS_USER_AURH = 'common.auth.handle.impl.chat_anonymous_user_token.ChatAnonymousUserToken'
CHAT_ANONYMOUS_USER_AURH = 'common.auth.handle.impl.chat_user_token.ChatUserToken'
APPLICATION_KEY_AUTH = 'common.auth.handle.impl.application_key.ApplicationKey'
AUTH_HANDLES = [
USER_TOKEN_AUTH
Expand Down
Loading