user.py 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  1. # coding=utf-8
  2. """
  3. @project: MaxKB
  4. @Author:虎虎
  5. @file: user.py
  6. @date:2025/4/14 19:18
  7. @desc:
  8. """
  9. import datetime
  10. import os
  11. import random
  12. import re
  13. from collections import defaultdict
  14. from django.core.cache import cache
  15. from django.core.mail.backends.smtp import EmailBackend
  16. from django.db import transaction
  17. from django.db.models import Q, QuerySet
  18. from rest_framework import serializers
  19. import uuid_utils.compat as uuid
  20. from common.constants.cache_version import Cache_Version
  21. from common.constants.exception_code_constants import ExceptionCodeConstants
  22. from common.constants.permission_constants import RoleConstants, Auth, ResourceAuthType, ResourcePermissionRole, \
  23. ResourcePermission
  24. from common.database_model_manage.database_model_manage import DatabaseModelManage
  25. from common.db.search import page_search
  26. from common.exception.app_exception import AppApiException
  27. from common.utils.common import valid_license, password_encrypt, get_random_chars
  28. from common.utils.rsa_util import decrypt
  29. from maxkb import settings
  30. from maxkb.conf import PROJECT_DIR
  31. from system_manage.models import SystemSetting, SettingType, AuthTargetType, WorkspaceUserResourcePermission
  32. from users.models import User
  33. from django.utils.translation import gettext_lazy as _, to_locale
  34. from django.core import validators
  35. from django.core.mail import send_mail
  36. from django.utils.translation import get_language
  37. PASSWORD_REGEX = re.compile(
  38. r"^" # 开始
  39. r"(?=.*[a-z])" # 至少一个小写字母
  40. r"(?=.*[-_!@#$%^&*`~.()+=])" # 至少一个指定的特殊字符
  41. r"(?:(?=.*[A-Z])|(?=.*\d))" # 至少一个大写字母 或 数字
  42. r"[a-zA-Z0-9-_!@#$%^&*`~.()+=]{6,20}" # 总长度6~20个合法字符
  43. r"$" # 结束
  44. )
  45. version, get_key = Cache_Version.SYSTEM.value
  46. class UserProfileResponse(serializers.ModelSerializer):
  47. is_edit_password = serializers.BooleanField(required=True, label=_('Is Edit Password'))
  48. permissions = serializers.ListField(required=True, label=_('permissions'))
  49. class Meta:
  50. model = User
  51. fields = ['id', 'username', 'nick_name', 'email', 'role', 'permissions', 'language', 'is_edit_password']
  52. class CreateUserSerializer(serializers.Serializer):
  53. username = serializers.CharField(required=True, label=_('Username'))
  54. password = serializers.CharField(required=True, label=_('Password'))
  55. email = serializers.EmailField(required=True, label=_('Email'))
  56. nick_name = serializers.CharField(required=False, label=_('Nick name'))
  57. phone = serializers.CharField(required=False, label=_('Phone'))
  58. source = serializers.CharField(required=False, label=_('Source'), default='LOCAL')
  59. defaultPermission = serializers.CharField(required=False, label=_('defaultPermission'))
  60. def is_workspace_manage(user_id: str, workspace_id: str):
  61. workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  62. role_permission_mapping_model = DatabaseModelManage.get_model("role_permission_mapping_model")
  63. is_x_pack_ee = workspace_user_role_mapping_model is not None and role_permission_mapping_model is not None
  64. if is_x_pack_ee:
  65. return QuerySet(workspace_user_role_mapping_model).select_related('role', 'user').filter(
  66. workspace_id=workspace_id, user_id=user_id,
  67. role__type=RoleConstants.WORKSPACE_MANAGE.value.__str__()).exists()
  68. return QuerySet(User).filter(id=user_id, role=RoleConstants.ADMIN.value.__str__()).exists()
  69. def get_workspace_list_by_user(user_id):
  70. get_workspace_list = DatabaseModelManage.get_model('get_workspace_list_by_user')
  71. license_is_valid = DatabaseModelManage.get_model('license_is_valid') or (lambda: False)
  72. if get_workspace_list is not None and license_is_valid():
  73. return get_workspace_list(user_id)
  74. return [{'id': 'default', 'name': 'default'}]
  75. class UserProfileSerializer(serializers.Serializer):
  76. @staticmethod
  77. def profile(user: User, auth: Auth):
  78. """
  79. 获取用户详情
  80. @param user: 用户对象
  81. @param auth: 认证对象
  82. @return:
  83. """
  84. workspace_list = get_workspace_list_by_user(user.id)
  85. user_role_relation_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  86. role_name = [user.role]
  87. if user_role_relation_model:
  88. user_role_relations = (
  89. user_role_relation_model.objects
  90. .filter(user_id=user.id)
  91. .select_related('role')
  92. .distinct('role_id')
  93. )
  94. role_name = [relation.role.role_name for relation in user_role_relations]
  95. return {
  96. 'id': user.id,
  97. 'username': user.username,
  98. 'nick_name': user.nick_name,
  99. 'email': user.email,
  100. 'source': user.source,
  101. 'role': auth.role_list,
  102. 'permissions': auth.permission_list,
  103. 'is_edit_password': user.password == 'd880e722c47a34d8e9fce789fc62389d' if user.source == 'LOCAL' else False,
  104. 'language': user.language,
  105. 'workspace_list': workspace_list,
  106. 'role_name': role_name
  107. }
  108. class UserInstanceSerializer(serializers.ModelSerializer):
  109. class Meta:
  110. model = User
  111. fields = ['id', 'username', 'email', 'phone', 'is_active', 'role', 'nick_name', 'create_time', 'update_time',
  112. 'source']
  113. class UserManageSerializer(serializers.Serializer):
  114. class UserInstance(serializers.Serializer):
  115. email = serializers.EmailField(
  116. required=True,
  117. label=_("Email"),
  118. validators=[validators.EmailValidator(
  119. message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
  120. code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code
  121. )]
  122. )
  123. username = serializers.CharField(
  124. required=True,
  125. label=_("Username"),
  126. max_length=64,
  127. min_length=4,
  128. validators=[
  129. validators.RegexValidator(
  130. regex=re.compile("^.{4,64}$"),
  131. message=_('Username must be 4-64 characters long')
  132. )
  133. ]
  134. )
  135. password = serializers.CharField(
  136. required=True,
  137. label=_("Password"),
  138. max_length=20,
  139. min_length=6,
  140. validators=[
  141. validators.RegexValidator(
  142. regex=PASSWORD_REGEX,
  143. message=_("密码必须为6-20个字符,且必须包含字母、数字和特殊字符。")
  144. )
  145. ]
  146. )
  147. nick_name = serializers.CharField(
  148. required=True,
  149. label=_("Nick name"),
  150. max_length=64,
  151. )
  152. phone = serializers.CharField(
  153. required=False,
  154. label=_("Phone"),
  155. max_length=20,
  156. allow_null=True,
  157. allow_blank=True
  158. )
  159. source = serializers.CharField(
  160. required=False,
  161. label=_("Source"),
  162. max_length=20,
  163. default="LOCAL"
  164. )
  165. def is_valid(self, *, raise_exception=True):
  166. super().is_valid(raise_exception=True)
  167. self._check_unique_username_and_email()
  168. def _check_unique_username_and_email(self):
  169. username = self.data.get('username')
  170. email = self.data.get('email')
  171. nick_name = self.data.get('nick_name')
  172. user = User.objects.filter(Q(username=username) | Q(email=email) | Q(nick_name=nick_name)).first()
  173. if user:
  174. if user.email == email:
  175. raise ExceptionCodeConstants.EMAIL_IS_EXIST.value.to_app_api_exception()
  176. if user.username == username:
  177. raise ExceptionCodeConstants.USERNAME_IS_EXIST.value.to_app_api_exception()
  178. if user.nick_name == nick_name:
  179. raise ExceptionCodeConstants.NICKNAME_IS_EXIST.value.to_app_api_exception()
  180. class Query(serializers.Serializer):
  181. username = serializers.CharField(
  182. required=False,
  183. label=_("Username"),
  184. max_length=64,
  185. allow_blank=True
  186. )
  187. nick_name = serializers.CharField(
  188. required=False,
  189. label=_("Nick Name"),
  190. max_length=64,
  191. allow_blank=True
  192. )
  193. email = serializers.CharField(
  194. required=False,
  195. label=_("Email"),
  196. allow_blank=True,
  197. )
  198. is_active = serializers.BooleanField(
  199. required=False,
  200. label=_("Is active"),
  201. )
  202. source = serializers.CharField(
  203. required=False,
  204. label=_("Source"),
  205. allow_blank=True,
  206. )
  207. def get_query_set(self):
  208. username = self.data.get('username')
  209. nick_name = self.data.get('nick_name')
  210. email = self.data.get('email')
  211. is_active = self.data.get('is_active', None)
  212. source = self.data.get('source', None)
  213. query_set = QuerySet(User)
  214. if username is not None:
  215. query_set = query_set.filter(username__contains=username)
  216. if nick_name is not None:
  217. query_set = query_set.filter(nick_name__contains=nick_name)
  218. if email is not None:
  219. query_set = query_set.filter(email__contains=email)
  220. if is_active is not None:
  221. query_set = query_set.filter(is_active=is_active)
  222. if source is not None:
  223. query_set = query_set.filter(source=source)
  224. query_set = query_set.order_by("-create_time")
  225. return query_set
  226. def list(self, with_valid=True):
  227. if with_valid:
  228. self.is_valid(raise_exception=True)
  229. return [{'id': user_model.id, 'username': user_model.username, 'email': user_model.email} for user_model in
  230. self.get_query_set()]
  231. def page(self, current_page: int, page_size: int, user_id: str, with_valid=True):
  232. if with_valid:
  233. self.is_valid(raise_exception=True)
  234. result = page_search(current_page, page_size,
  235. self.get_query_set(),
  236. post_records_handler=lambda u: UserInstanceSerializer(u).data)
  237. role_model = DatabaseModelManage.get_model("role_model")
  238. user_role_relation_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  239. def _get_user_roles(user_ids, is_admin=True):
  240. workspace_model = DatabaseModelManage.get_model("workspace_model")
  241. if not (role_model and user_role_relation_model and workspace_model):
  242. return {}
  243. workspace_mapping = {str(workspace_model.id): workspace_model.name for workspace_model in
  244. workspace_model.objects.all()}
  245. # 获取所有相关角色关系,并预加载角色信息
  246. user_role_relations = (
  247. user_role_relation_model.objects
  248. .filter(user_id__in=user_ids)
  249. .select_related('role')
  250. .distinct('user_id', 'role_id', 'workspace_id') # 确保组合唯一性
  251. )
  252. # 构建用户ID到角色名称列表的映射
  253. user_role_mapping = defaultdict(set) # 使用 set 去重
  254. # 构建用户ID到角色ID与工作空间ID映射
  255. user_role_setting_mapping = defaultdict(lambda: defaultdict(list))
  256. user_role_workspace_mapping = defaultdict(lambda: defaultdict(list))
  257. for relation in user_role_relations:
  258. user_id = str(relation.user_id)
  259. role_id = relation.role_id
  260. workspace_id = relation.workspace_id
  261. if not is_admin and relation.role.type == RoleConstants.ADMIN.name:
  262. continue
  263. user_role_mapping[user_id].add(relation.role.role_name)
  264. user_role_setting_mapping[user_id][role_id].append(workspace_id)
  265. user_role_workspace_mapping[user_id][relation.role.role_name].append(
  266. workspace_mapping.get(workspace_id, workspace_id))
  267. # 将 set 转换为 list 以符合返回格式
  268. user_role_mapping = {uid: list(roles) for uid, roles in user_role_mapping.items()}
  269. # 转换为所需的结构
  270. result_user_role_setting_mapping = {
  271. user_id: [{"role_id": role_id, "workspace_ids": workspace_ids}
  272. for role_id, workspace_ids in roles.items()]
  273. for user_id, roles in user_role_setting_mapping.items()
  274. }
  275. result_user_role_workspace_mapping = {
  276. user_id: {role_name: workspace_names
  277. for role_name, workspace_names in roles.items()}
  278. for user_id, roles in user_role_workspace_mapping.items()
  279. }
  280. return user_role_mapping, result_user_role_setting_mapping, result_user_role_workspace_mapping
  281. if role_model and user_role_relation_model:
  282. # 获取当前用户的所有角色 判断是不是内置的系统管理员
  283. is_admin = user_role_relation_model.objects.filter(user_id=user_id,
  284. role_id=RoleConstants.ADMIN.name).exists()
  285. user_ids = [user['id'] for user in result['records']]
  286. user_role_mapping, user_role_setting_mapping, user_role_workspace_mapping = _get_user_roles(user_ids,
  287. is_admin)
  288. # 将角色信息添加回用户数据中
  289. for user in result['records']:
  290. user_id = str(user['id'])
  291. user['role_name'] = user_role_mapping.get(user_id, [])
  292. user['role_setting'] = user_role_setting_mapping.get(user_id, [])
  293. user['role_workspace'] = user_role_workspace_mapping.get(user_id, [])
  294. return result
  295. @transaction.atomic
  296. def save(self, instance, user_id, with_valid=True):
  297. if with_valid:
  298. if instance.get('encrypted'):
  299. instance['password'] = decrypt(instance.get('password'))
  300. self.UserInstance(data=instance).is_valid(raise_exception=True)
  301. user = User(
  302. id=uuid.uuid7(),
  303. email=instance.get('email'),
  304. phone=instance.get('phone', ''),
  305. nick_name=instance.get('nick_name', ''),
  306. username=instance.get('username'),
  307. password=password_encrypt(instance.get('password')),
  308. role=RoleConstants.USER.name,
  309. source=instance.get('source', 'LOCAL'),
  310. is_active=True
  311. )
  312. update_user_role(instance, user, user_id)
  313. set_default_permission(user.id, instance)
  314. user.save()
  315. return UserInstanceSerializer(user).data
  316. class UserEditInstance(serializers.Serializer):
  317. email = serializers.EmailField(
  318. required=False,
  319. label=_("Email"),
  320. validators=[validators.EmailValidator(
  321. message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
  322. code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code
  323. )]
  324. )
  325. nick_name = serializers.CharField(
  326. required=False,
  327. label=_("Name"),
  328. max_length=64,
  329. )
  330. phone = serializers.CharField(
  331. required=False,
  332. label=_("Phone"),
  333. max_length=20,
  334. allow_null=True,
  335. allow_blank=True
  336. )
  337. is_active = serializers.BooleanField(
  338. required=False,
  339. label=_("Is Active")
  340. )
  341. def is_valid(self, *, user_id=None, raise_exception=False):
  342. super().is_valid(raise_exception=True)
  343. self._check_unique_email(user_id)
  344. self._check_unique_nick_name(user_id)
  345. def _check_unique_nick_name(self, user_id):
  346. nick_name = self.data.get('nick_name')
  347. if nick_name and User.objects.filter(nick_name=nick_name).exclude(id=user_id).exists():
  348. raise AppApiException(1008, _('Nickname is already in use'))
  349. def _check_unique_email(self, user_id):
  350. email = self.data.get('email')
  351. if email and User.objects.filter(email=email).exclude(id=user_id).exists():
  352. raise AppApiException(1004, _('Email is already in use'))
  353. class RePasswordInstance(serializers.Serializer):
  354. password = serializers.CharField(
  355. required=True,
  356. label=_("Password"),
  357. max_length=20,
  358. min_length=6,
  359. validators=[
  360. validators.RegexValidator(
  361. regex=PASSWORD_REGEX,
  362. message=_("密码必须为6-20个字符,且必须包含字母、数字和特殊字符。")
  363. )
  364. ]
  365. )
  366. re_password = serializers.CharField(
  367. required=True,
  368. label=_("Re Password"),
  369. validators=[
  370. validators.RegexValidator(
  371. regex=PASSWORD_REGEX,
  372. message=_(
  373. "确认密码必须为6-20个字符,且必须包含字母、数字和特殊字符。"
  374. )
  375. )
  376. ]
  377. )
  378. def is_valid(self, *, raise_exception=False):
  379. super().is_valid(raise_exception=True)
  380. self._check_passwords_match()
  381. def _check_passwords_match(self):
  382. if self.data.get('password') != self.data.get('re_password'):
  383. raise ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.to_app_api_exception()
  384. class Operate(serializers.Serializer):
  385. id = serializers.UUIDField(required=True, label=_('User ID'))
  386. def is_valid(self, *, raise_exception=False):
  387. super().is_valid(raise_exception=True)
  388. self._check_user_exists()
  389. def _check_user_exists(self):
  390. if not User.objects.filter(id=self.data.get('id')).exists():
  391. raise AppApiException(1004, _('User does not exist'))
  392. @transaction.atomic
  393. def delete(self, with_valid=True):
  394. if with_valid:
  395. self.is_valid(raise_exception=True)
  396. self._check_not_admin()
  397. user_id = self.data.get('id')
  398. # TODO 需要删除授权关系
  399. User.objects.filter(id=user_id).delete()
  400. return True
  401. def _check_not_admin(self):
  402. user = User.objects.filter(id=self.data.get('id')).first()
  403. if user.role == RoleConstants.ADMIN.name or str(user.id) == 'f0dd8f71-e4ee-11ee-8c84-a8a1595801ab':
  404. raise AppApiException(1004, _('Unable to delete administrator'))
  405. def edit(self, instance, user_id, with_valid=True):
  406. if with_valid:
  407. self.is_valid(raise_exception=True)
  408. UserManageSerializer.UserEditInstance(data=instance).is_valid(user_id=self.data.get('id'),
  409. raise_exception=True)
  410. user = User.objects.filter(id=self.data.get('id')).first()
  411. self._check_admin_modification(user, instance)
  412. self._update_user_fields(user, instance)
  413. update_user_role(instance, user, user_id)
  414. user.save()
  415. return UserInstanceSerializer(user).data
  416. @staticmethod
  417. def _check_admin_modification(user, instance):
  418. if user.role == RoleConstants.ADMIN.name and 'is_active' in instance and instance.get(
  419. 'is_active') is not None:
  420. raise AppApiException(1004, _('Cannot modify administrator status'))
  421. @staticmethod
  422. def _update_user_fields(user, instance):
  423. update_keys = ['email', 'nick_name', 'phone', 'is_active']
  424. for key in update_keys:
  425. if key in instance and instance.get(key) is not None:
  426. setattr(user, key, instance.get(key))
  427. def one(self, with_valid=True):
  428. if with_valid:
  429. self.is_valid(raise_exception=True)
  430. user = User.objects.filter(id=self.data.get('id')).first()
  431. workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  432. if workspace_user_role_mapping_model:
  433. role_setting = {}
  434. workspace_user_role_mapping_list = QuerySet(workspace_user_role_mapping_model).filter(
  435. user_id=user.id)
  436. for workspace_user_role_mapping in workspace_user_role_mapping_list:
  437. role_id = workspace_user_role_mapping.role_id
  438. workspace_id = workspace_user_role_mapping.workspace_id
  439. if role_id not in role_setting:
  440. role_setting[role_id] = []
  441. role_setting[role_id].append(workspace_id)
  442. return {
  443. 'id': user.id,
  444. 'username': user.username,
  445. 'email': user.email,
  446. 'phone': user.phone,
  447. 'nick_name': user.nick_name,
  448. 'is_active': user.is_active,
  449. 'role_setting': role_setting
  450. }
  451. return UserInstanceSerializer(user).data
  452. def re_password(self, instance, with_valid=True):
  453. if with_valid:
  454. self.is_valid(raise_exception=True)
  455. UserManageSerializer.RePasswordInstance(data=instance).is_valid(raise_exception=True)
  456. user = User.objects.filter(id=self.data.get('id')).first()
  457. user.password = password_encrypt(instance.get('password'))
  458. user.save()
  459. return True
  460. def get_user_list(self, workspace_id):
  461. """
  462. 获取用户列表
  463. :param workspace_id: 工作空间ID
  464. :return: 用户列表
  465. """
  466. workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  467. if workspace_user_role_mapping_model:
  468. user_ids = (
  469. workspace_user_role_mapping_model.objects
  470. .filter(workspace_id=workspace_id)
  471. .values_list('user_id', flat=True)
  472. .distinct()
  473. )
  474. else:
  475. user_ids = User.objects.values_list('id', flat=True)
  476. users = User.objects.filter(id__in=user_ids).values('id', 'nick_name')
  477. return list(users)
  478. def get_user_members(self, workspace_id):
  479. """
  480. 获取工作空间成员列表
  481. :param workspace_id: 工作空间ID
  482. :return: 成员列表
  483. """
  484. role_model = DatabaseModelManage.get_model("role_model")
  485. user_role_relation_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  486. if user_role_relation_model and role_model:
  487. user_role_relations = (
  488. user_role_relation_model.objects
  489. .filter(workspace_id=workspace_id, role__type='USER')
  490. .select_related('role', 'user')
  491. )
  492. user_dict = {}
  493. for relation in user_role_relations:
  494. user_id = relation.user.id
  495. if user_id not in user_dict:
  496. user_dict[user_id] = {
  497. 'id': user_id,
  498. 'nick_name': relation.user.nick_name,
  499. 'email': relation.user.email,
  500. 'roles': [relation.role.role_name]
  501. }
  502. else:
  503. user_dict[user_id]['roles'].append(relation.role.role_name)
  504. # 将字典值转换为列表形式
  505. return list(user_dict.values())
  506. user_list = User.objects.exclude(role=RoleConstants.ADMIN.name)
  507. return [
  508. {
  509. 'id': user.id,
  510. 'nick_name': user.nick_name,
  511. 'email': user.email,
  512. 'roles': [RoleConstants.USER.name]
  513. } for user in user_list
  514. ]
  515. class BatchDelete(serializers.Serializer):
  516. ids = serializers.ListField(required=True, label=_('User IDs'))
  517. def batch_delete(self, with_valid=True):
  518. user_ids = self.data.get('ids')
  519. if not user_ids:
  520. raise AppApiException(1004, _('User IDs cannot be empty'))
  521. User.objects.filter(id__in=user_ids).exclude(id='f0dd8f71-e4ee-11ee-8c84-a8a1595801ab').delete()
  522. return True
  523. def get_all_user_list(self, nick_name=None):
  524. query_set = User.objects.all()
  525. if nick_name:
  526. query_set = query_set.filter(nick_name__contains=nick_name)
  527. users = query_set.values('id', 'nick_name', 'username')[:200]
  528. return list(users)
  529. def update_user_role(instance, user, user_id=None):
  530. workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
  531. if workspace_user_role_mapping_model:
  532. role_setting = instance.get('role_setting')
  533. license_is_valid = DatabaseModelManage.get_model('license_is_valid') or (lambda: False)
  534. license_is_valid = license_is_valid() if license_is_valid() is not None else False
  535. if not role_setting or (len(role_setting) == 1
  536. and role_setting[0].get('role_id') == ''
  537. and len(role_setting[0].get('workspace_ids', [])) == 0):
  538. if not license_is_valid:
  539. workspace_user_role_mapping_model.objects.create(
  540. id=uuid.uuid7(),
  541. user_id=user.id,
  542. role_id=RoleConstants.USER.name,
  543. workspace_id='default'
  544. )
  545. return
  546. is_admin = workspace_user_role_mapping_model.objects.filter(user_id=user_id,
  547. role_id=RoleConstants.ADMIN.name).exists()
  548. if str(user.id) == 'f0dd8f71-e4ee-11ee-8c84-a8a1595801ab':
  549. # 需要判断当前角色的权限 不能删除系统管理员 空间管理员 普通管理员等角色
  550. # role_setting是一个数组 结构式 [{role_id:1,workspace_ids:[1,2]}]
  551. # 如果role_id不包含ADMIN 就直接报错 如果WORKSPACE_MANAGE 或者USER 必须判断workspace_ids是否包含默认工作空间 不包含就报错
  552. admin_role_id = RoleConstants.ADMIN.name
  553. workspace_manage_role_id = RoleConstants.WORKSPACE_MANAGE.name
  554. # 判断内置的三个角色是不是不在
  555. current_role_ids = {item['role_id'] for item in role_setting}
  556. initial_role = [admin_role_id, workspace_manage_role_id, RoleConstants.USER.name]
  557. if not set(initial_role).issubset(current_role_ids):
  558. raise AppApiException(1004, _("Cannot delete built-in role"))
  559. if not any(item['role_id'] == str(admin_role_id) for item in role_setting):
  560. raise AppApiException(1004, _("Cannot delete built-in role"))
  561. # 验证 WORKSPACE_MANAGE 或 USER 是否包含默认工作空间
  562. default_workspace_id = 'default'
  563. for item in role_setting:
  564. role_id = item['role_id']
  565. workspace_ids = item.get('workspace_ids', [])
  566. if role_id == str(workspace_manage_role_id) or role_id == str(RoleConstants.USER.value):
  567. if default_workspace_id not in workspace_ids:
  568. raise AppApiException(1004, _("Cannot delete built-in role"))
  569. if is_admin:
  570. workspace_user_role_mapping_model.objects.filter(user_id=user.id).delete()
  571. else:
  572. workspace_user_role_mapping_model.objects.filter(user_id=user.id).exclude(
  573. role__type=RoleConstants.ADMIN.name).delete()
  574. relations = set()
  575. for item in role_setting:
  576. role_id = item['role_id']
  577. workspace_ids = item['workspace_ids'] if item['workspace_ids'] else ['None']
  578. for workspace_id in workspace_ids:
  579. relations.add((role_id, workspace_id))
  580. for role_id, workspace_id in relations:
  581. workspace_user_role_mapping_model.objects.create(
  582. id=uuid.uuid7(),
  583. role_id=role_id,
  584. workspace_id=workspace_id,
  585. user_id=user.id
  586. )
  587. permission_version, permission_get_key = Cache_Version.PERMISSION_LIST.value
  588. cache.delete(permission_get_key(str(user.id)), version=permission_version)
  589. def set_default_permission(user_id, instance):
  590. """
  591. 为用户设置默认权限
  592. """
  593. default_permission = instance.get('defaultPermission', 'NOT_AUTH')
  594. # 获取工作空间ID列表
  595. workspace_ids = _get_workspace_ids(instance, default_permission)
  596. if not workspace_ids:
  597. return
  598. # 根据权限类型确定认证类型
  599. auth_type = (ResourceAuthType.ROLE
  600. if default_permission == ResourceAuthType.ROLE
  601. else ResourceAuthType.RESOURCE_PERMISSION_GROUP)
  602. # 设置根目录权限
  603. _set_root_permissions(user_id, workspace_ids)
  604. # 如果是无权限设置,直接返回
  605. if default_permission == 'NOT_AUTH':
  606. return
  607. # 设置具体资源权限
  608. _set_resource_permissions(user_id, workspace_ids, default_permission, auth_type)
  609. def _get_workspace_ids(instance, default_permission):
  610. """
  611. 获取工作空间ID列表
  612. """
  613. role_setting_model = DatabaseModelManage.get_model("role_model")
  614. if not role_setting_model:
  615. return ['default']
  616. # 检查许可证有效性
  617. license_is_valid = DatabaseModelManage.get_model('license_is_valid') or (lambda: False)
  618. if default_permission == ResourceAuthType.ROLE and not license_is_valid():
  619. return []
  620. role_setting = instance.get('role_setting')
  621. if not role_setting:
  622. return ['default']
  623. # 获取用户角色的工作空间ID
  624. all_role_ids = [item['role_id'] for item in role_setting]
  625. user_role_ids = set(role_setting_model.objects.filter(
  626. id__in=all_role_ids,
  627. type=RoleConstants.USER.name
  628. ).values_list('id', flat=True))
  629. workspace_ids = set()
  630. for item in role_setting:
  631. role_id = item['role_id']
  632. if role_id in user_role_ids:
  633. workspace_ids.update(item.get('workspace_ids', []))
  634. return list(workspace_ids) if workspace_ids else []
  635. def _set_root_permissions(user_id, workspace_ids):
  636. """
  637. 设置根目录权限(默认为查看权限)
  638. """
  639. root_permissions = []
  640. for ws in workspace_ids:
  641. root_permissions.extend([
  642. WorkspaceUserResourcePermission(
  643. target=ws,
  644. auth_target_type=auth_target_type,
  645. permission_list=[ResourcePermission.VIEW],
  646. workspace_id=ws,
  647. user_id=user_id,
  648. auth_type=ResourceAuthType.RESOURCE_PERMISSION_GROUP
  649. )
  650. for auth_target_type in [
  651. AuthTargetType.APPLICATION.value,
  652. AuthTargetType.KNOWLEDGE.value,
  653. AuthTargetType.TOOL.value
  654. ]
  655. ])
  656. _batch_create_permissions(root_permissions)
  657. def _set_resource_permissions(user_id, workspace_ids, default_permission, auth_type):
  658. """
  659. 设置具体资源权限
  660. """
  661. # 批量查询资源并按工作空间分组
  662. resource_maps = _get_resource_maps(workspace_ids)
  663. # 构造权限实例
  664. instances = []
  665. for ws in workspace_ids:
  666. instances.extend(_create_resource_permission_instances(
  667. ws, resource_maps, user_id, default_permission, auth_type))
  668. # 批量创建权限
  669. _batch_create_permissions(instances)
  670. def _get_resource_maps(workspace_ids):
  671. """
  672. 获取各类型资源按工作空间的映射
  673. """
  674. from application.models import Application, ApplicationFolder
  675. from knowledge.models import Knowledge, KnowledgeFolder
  676. from tools.models import Tool, ToolFolder
  677. from models_provider.models import Model
  678. from collections import defaultdict
  679. resource_maps = {
  680. 'apps': defaultdict(list),
  681. 'app_folders': defaultdict(list),
  682. 'knowledge': defaultdict(list),
  683. 'knowledge_folders': defaultdict(list),
  684. 'tools': defaultdict(list),
  685. 'tool_folders': defaultdict(list),
  686. 'models': defaultdict(list)
  687. }
  688. # 查询应用资源
  689. for ws, rid in Application.objects.filter(workspace_id__in=workspace_ids).values_list('workspace_id', 'id'):
  690. resource_maps['apps'][ws].append(rid)
  691. for ws, fid in ApplicationFolder.objects.filter(workspace_id__in=workspace_ids).exclude(
  692. id__in=workspace_ids).values_list('workspace_id', 'id'):
  693. resource_maps['app_folders'][ws].append(fid)
  694. # 查询知识库资源
  695. for ws, kid in Knowledge.objects.filter(workspace_id__in=workspace_ids).values_list('workspace_id', 'id'):
  696. resource_maps['knowledge'][ws].append(kid)
  697. for ws, kfid in KnowledgeFolder.objects.filter(workspace_id__in=workspace_ids).exclude(
  698. id__in=workspace_ids).values_list('workspace_id', 'id'):
  699. resource_maps['knowledge_folders'][ws].append(kfid)
  700. # 查询工具资源
  701. for ws, tid in Tool.objects.filter(workspace_id__in=workspace_ids).values_list('workspace_id', 'id'):
  702. resource_maps['tools'][ws].append(tid)
  703. for ws, tfid in ToolFolder.objects.filter(workspace_id__in=workspace_ids).exclude(
  704. id__in=workspace_ids).values_list('workspace_id', 'id'):
  705. resource_maps['tool_folders'][ws].append(tfid)
  706. # 查询模型资源
  707. for ws, mid in Model.objects.filter(workspace_id__in=workspace_ids).values_list('workspace_id', 'id'):
  708. resource_maps['models'][ws].append(mid)
  709. return resource_maps
  710. def _create_resource_permission_instances(workspace_id, resource_maps, user_id, permission, auth_type):
  711. """
  712. 创建资源权限实例列表
  713. """
  714. instances = []
  715. if permission == ResourcePermission.MANAGE:
  716. permission = [ResourcePermission.VIEW, ResourcePermission.MANAGE]
  717. else:
  718. permission = [permission]
  719. # 应用权限
  720. for rid in resource_maps['apps'].get(workspace_id, []):
  721. instances.append(WorkspaceUserResourcePermission(
  722. target=rid,
  723. auth_target_type=AuthTargetType.APPLICATION.value,
  724. permission_list=permission,
  725. workspace_id=workspace_id,
  726. user_id=user_id,
  727. auth_type=auth_type
  728. ))
  729. # 应用文件夹权限
  730. for fid in resource_maps['app_folders'].get(workspace_id, []):
  731. instances.append(WorkspaceUserResourcePermission(
  732. target=fid,
  733. auth_target_type=AuthTargetType.APPLICATION.value,
  734. permission_list=permission,
  735. workspace_id=workspace_id,
  736. user_id=user_id,
  737. auth_type=auth_type
  738. ))
  739. # 知识库权限
  740. for kid in resource_maps['knowledge'].get(workspace_id, []):
  741. instances.append(WorkspaceUserResourcePermission(
  742. target=kid,
  743. auth_target_type=AuthTargetType.KNOWLEDGE.value,
  744. permission_list=permission,
  745. workspace_id=workspace_id,
  746. user_id=user_id,
  747. auth_type=auth_type
  748. ))
  749. # 知识库文件夹权限
  750. for kf in resource_maps['knowledge_folders'].get(workspace_id, []):
  751. instances.append(WorkspaceUserResourcePermission(
  752. target=kf,
  753. auth_target_type=AuthTargetType.KNOWLEDGE.value,
  754. permission_list=permission,
  755. workspace_id=workspace_id,
  756. user_id=user_id,
  757. auth_type=auth_type
  758. ))
  759. # 工具权限
  760. for tid in resource_maps['tools'].get(workspace_id, []):
  761. instances.append(WorkspaceUserResourcePermission(
  762. target=tid,
  763. auth_target_type=AuthTargetType.TOOL.value,
  764. permission_list=permission,
  765. workspace_id=workspace_id,
  766. user_id=user_id,
  767. auth_type=auth_type
  768. ))
  769. # 工具文件夹权限
  770. for tf in resource_maps['tool_folders'].get(workspace_id, []):
  771. instances.append(WorkspaceUserResourcePermission(
  772. target=tf,
  773. auth_target_type=AuthTargetType.TOOL.value,
  774. permission_list=permission,
  775. workspace_id=workspace_id,
  776. user_id=user_id,
  777. auth_type=auth_type
  778. ))
  779. # 模型权限
  780. for mid in resource_maps['models'].get(workspace_id, []):
  781. instances.append(WorkspaceUserResourcePermission(
  782. target=mid,
  783. auth_target_type=AuthTargetType.MODEL.value,
  784. permission_list=permission,
  785. workspace_id=workspace_id,
  786. user_id=user_id,
  787. auth_type=auth_type
  788. ))
  789. return instances
  790. def _batch_create_permissions(instances, batch_size=500):
  791. """
  792. 批量创建权限实例
  793. """
  794. if not instances:
  795. return
  796. objs = WorkspaceUserResourcePermission.objects
  797. for i in range(0, len(instances), batch_size):
  798. objs.bulk_create(instances[i:i + batch_size])
  799. class RePasswordSerializer(serializers.Serializer):
  800. email = serializers.EmailField(
  801. required=True,
  802. label=_("Email"),
  803. validators=[validators.EmailValidator(message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
  804. code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code)])
  805. code = serializers.CharField(required=True, label=_("Code"))
  806. password = serializers.CharField(
  807. required=True,
  808. label=_("Password"),
  809. max_length=20,
  810. min_length=6,
  811. validators=[
  812. validators.RegexValidator(
  813. regex=PASSWORD_REGEX,
  814. message=_(
  815. "密码必须为6-20个字符,且必须包含字母、数字和特殊字符。"
  816. )
  817. )
  818. ]
  819. )
  820. re_password = serializers.CharField(
  821. required=True,
  822. label=_("Re Password"),
  823. validators=[
  824. validators.RegexValidator(
  825. regex=PASSWORD_REGEX,
  826. message=_(
  827. "确认密码必须为6-20个字符,且必须包含字母、数字和特殊字符。"
  828. )
  829. )
  830. ]
  831. )
  832. class Meta:
  833. model = User
  834. fields = '__all__'
  835. def is_valid(self, *, raise_exception=False):
  836. super().is_valid(raise_exception=True)
  837. email = self.data.get("email")
  838. cache_code = cache.get(get_key(email + ':reset_password'), version=version)
  839. if self.data.get('password') != self.data.get('re_password'):
  840. raise AppApiException(ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.code,
  841. ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.message)
  842. if cache_code != self.data.get('code'):
  843. raise AppApiException(ExceptionCodeConstants.CODE_ERROR.value.code,
  844. ExceptionCodeConstants.CODE_ERROR.value.message)
  845. return True
  846. def reset_password(self):
  847. """
  848. 修改密码
  849. :return: 是否成功
  850. """
  851. if self.is_valid():
  852. email = self.data.get("email")
  853. QuerySet(User).filter(email=email).update(
  854. password=password_encrypt(self.data.get('password')))
  855. code_cache_key = email + ":reset_password"
  856. cache.delete(get_key(code_cache_key), version=version)
  857. return True
  858. class ResetCurrentUserPassword(serializers.Serializer):
  859. password = serializers.CharField(
  860. required=True,
  861. label=_("Password"),
  862. max_length=20,
  863. min_length=6,
  864. validators=[
  865. validators.RegexValidator(
  866. regex=PASSWORD_REGEX,
  867. message=_(
  868. "密码必须为6-20个字符,且必须包含字母、数字和特殊字符。"
  869. )
  870. )
  871. ]
  872. )
  873. re_password = serializers.CharField(
  874. required=True,
  875. label=_("Re Password"),
  876. validators=[
  877. validators.RegexValidator(
  878. regex=PASSWORD_REGEX,
  879. message=_(
  880. "确认密码必须为6-20个字符,且必须包含字母、数字和特殊字符。"
  881. )
  882. )
  883. ]
  884. )
  885. class Meta:
  886. model = User
  887. fields = '__all__'
  888. def is_valid(self, *, raise_exception=False):
  889. super().is_valid(raise_exception=True)
  890. if self.data.get('password') != self.data.get('re_password'):
  891. raise AppApiException(ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.code,
  892. ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.message)
  893. return True
  894. def reset_password(self, user_id: str):
  895. """
  896. 修改密码
  897. :return: 是否成功
  898. """
  899. if self.is_valid():
  900. QuerySet(User).filter(id=user_id).update(
  901. password=password_encrypt(self.data.get('password')))
  902. return True
  903. class SendEmailSerializer(serializers.Serializer):
  904. email = serializers.EmailField(
  905. required=True
  906. , label=_("Email"),
  907. validators=[validators.EmailValidator(message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
  908. code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code)])
  909. type = serializers.CharField(required=True, label=_("Type"), validators=[
  910. validators.RegexValidator(regex=re.compile("^register|reset_password$"),
  911. message=_("The type only supports register|reset_password"), code=500)
  912. ])
  913. class Meta:
  914. model = User
  915. fields = '__all__'
  916. def is_valid(self, *, raise_exception=False):
  917. super().is_valid(raise_exception=raise_exception)
  918. code_cache_key = self.data.get('email') + ":" + self.data.get("type")
  919. code_cache_key_lock = code_cache_key + "_lock"
  920. ttl = cache.ttl(code_cache_key_lock, version=version)
  921. if ttl is not None and ttl > 0:
  922. raise AppApiException(500, _("Do not send emails again within {seconds} seconds").format(
  923. seconds=int(ttl.total_seconds())))
  924. return True
  925. def send(self):
  926. """
  927. 发送邮件
  928. :return: 是否发送成功
  929. :exception 发送失败异常
  930. """
  931. email = self.data.get("email")
  932. state = self.data.get("type")
  933. # 生成随机验证码
  934. code = "".join(list(map(lambda i: random.choice(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
  935. ]), range(6))))
  936. # 获取邮件模板
  937. language = get_language()
  938. file = open(
  939. os.path.join(PROJECT_DIR, "apps", "common", 'template', f'email_template_{to_locale(language)}.html'), "r",
  940. encoding='utf-8')
  941. content = file.read()
  942. file.close()
  943. code_cache_key = email + ":" + state
  944. code_cache_key_lock = code_cache_key + "_lock"
  945. # 设置缓存
  946. cache.set(get_key(code_cache_key_lock), code, timeout=60, version=version)
  947. system_setting = QuerySet(SystemSetting).filter(type=SettingType.EMAIL.value).first()
  948. if system_setting is None:
  949. cache.delete(get_key(code_cache_key_lock), version=version)
  950. raise AppApiException(1004,
  951. _("The email service has not been set up. Please contact the administrator to set up the email service in [Email Settings]."))
  952. try:
  953. connection = EmailBackend(system_setting.meta.get("email_host"),
  954. system_setting.meta.get('email_port'),
  955. system_setting.meta.get('email_host_user'),
  956. system_setting.meta.get('email_host_password'),
  957. system_setting.meta.get('email_use_tls'),
  958. False,
  959. system_setting.meta.get('email_use_ssl')
  960. )
  961. # 发送邮件
  962. send_mail(_('【Intelligent knowledge base question and answer system-{action}】').format(
  963. action=_('User registration') if state == 'register' else _('Change password')),
  964. '',
  965. html_message=f'{content.replace("${code}", code)}',
  966. from_email=system_setting.meta.get('from_email'),
  967. recipient_list=[email], fail_silently=False, connection=connection)
  968. except Exception as e:
  969. cache.delete(get_key(code_cache_key_lock))
  970. return True
  971. cache.set(get_key(code_cache_key), code, timeout=60 * 30, version=version)
  972. return True
  973. class CheckCodeSerializer(serializers.Serializer):
  974. """
  975. 校验验证码
  976. """
  977. email = serializers.EmailField(
  978. required=True,
  979. label=_("Email"),
  980. validators=[validators.EmailValidator(message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
  981. code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code)])
  982. code = serializers.CharField(required=True, label=_("Verification code"))
  983. type = serializers.CharField(required=True,
  984. label=_("Type"),
  985. validators=[
  986. validators.RegexValidator(regex=re.compile("^register|reset_password$"),
  987. message=_(
  988. "The type only supports register|reset_password"),
  989. code=500)
  990. ])
  991. def is_valid(self, *, raise_exception=False):
  992. super().is_valid()
  993. value = cache.get(get_key(self.data.get("email") + ":" + self.data.get("type")), version=version)
  994. if value is None or value != self.data.get("code"):
  995. raise ExceptionCodeConstants.CODE_ERROR.value.to_app_api_exception()
  996. return True
  997. class SwitchLanguageSerializer(serializers.Serializer):
  998. user_id = serializers.UUIDField(required=True, label=_('user id'))
  999. language = serializers.CharField(required=True, label=_('language'))
  1000. def switch(self):
  1001. self.is_valid(raise_exception=True)
  1002. language = self.data.get('language')
  1003. support_language_list = ['zh-CN', 'zh-Hant', 'en-US']
  1004. if not support_language_list.__contains__(language):
  1005. raise AppApiException(500, _('language only support:') + ','.join(support_language_list))
  1006. QuerySet(User).filter(id=self.data.get('user_id')).update(language=language)