user.py 47 KB

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