views.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
  2. # Copyright: (c) <spug.dev@gmail.com>
  3. # Released under the AGPL-3.0 License.
  4. from django.core.cache import cache
  5. from django.views.generic import View
  6. from libs import JsonParser, Argument, human_datetime, json_response
  7. from libs.utils import get_request_real_ip, generate_random_str
  8. from libs.spug import send_login_wx_code
  9. from apps.account.models import User, Role, History
  10. from apps.setting.utils import AppSetting
  11. from libs.ldap import LDAP
  12. import ipaddress
  13. import time
  14. import uuid
  15. import json
  16. class UserView(View):
  17. def get(self, request):
  18. users = []
  19. for u in User.objects.filter(deleted_by_id__isnull=True):
  20. tmp = u.to_dict(excludes=('access_token', 'password_hash'))
  21. tmp['role_ids'] = [x.id for x in u.roles.all()]
  22. tmp['password'] = '******'
  23. users.append(tmp)
  24. return json_response(users)
  25. def post(self, request):
  26. form, error = JsonParser(
  27. Argument('id', type=int, required=False),
  28. Argument('username', help='请输入登录名'),
  29. Argument('password', help='请输入密码'),
  30. Argument('nickname', help='请输入姓名'),
  31. Argument('role_ids', type=list, default=[]),
  32. Argument('wx_token', required=False),
  33. ).parse(request.body)
  34. if error is None:
  35. user = User.objects.filter(username=form.username, deleted_by_id__isnull=True).first()
  36. if user and (not form.id or form.id != user.id):
  37. return json_response(error=f'已存在登录名为【{form.username}】的用户')
  38. role_ids, password = form.pop('role_ids'), form.pop('password')
  39. if form.id:
  40. user = User.objects.get(pk=form.id)
  41. user.update_by_dict(form)
  42. else:
  43. user = User.objects.create(
  44. password_hash=User.make_password(password),
  45. created_by=request.user,
  46. **form
  47. )
  48. user.roles.set(role_ids)
  49. return json_response(error=error)
  50. def patch(self, request):
  51. form, error = JsonParser(
  52. Argument('id', type=int, help='参数错误'),
  53. Argument('password', required=False),
  54. Argument('is_active', type=bool, required=False),
  55. ).parse(request.body)
  56. if error is None:
  57. user = User.objects.get(pk=form.id)
  58. if form.password:
  59. user.token_expired = 0
  60. user.password_hash = User.make_password(form.pop('password'))
  61. if form.is_active is not None:
  62. user.is_active = form.is_active
  63. cache.delete(user.username)
  64. user.save()
  65. return json_response(error=error)
  66. def delete(self, request):
  67. form, error = JsonParser(
  68. Argument('id', type=int, help='请指定操作对象')
  69. ).parse(request.GET)
  70. if error is None:
  71. user = User.objects.filter(pk=form.id).first()
  72. if user:
  73. if user.type == 'ldap':
  74. return json_response(error='ldap账户无法删除,请使用禁用功能来禁止该账户访问系统')
  75. if user.id == request.user.id:
  76. return json_response(error='无法删除当前登录账户')
  77. user.is_active = True
  78. user.deleted_at = human_datetime()
  79. user.deleted_by = request.user
  80. user.save()
  81. return json_response(error=error)
  82. class RoleView(View):
  83. def get(self, request):
  84. roles = Role.objects.all()
  85. return json_response(roles)
  86. def post(self, request):
  87. form, error = JsonParser(
  88. Argument('id', type=int, required=False),
  89. Argument('name', help='请输入角色名称'),
  90. Argument('desc', required=False)
  91. ).parse(request.body)
  92. if error is None:
  93. if form.id:
  94. Role.objects.filter(pk=form.id).update(**form)
  95. else:
  96. Role.objects.create(created_by=request.user, **form)
  97. return json_response(error=error)
  98. def patch(self, request):
  99. form, error = JsonParser(
  100. Argument('id', type=int, help='参数错误'),
  101. Argument('page_perms', type=dict, required=False),
  102. Argument('deploy_perms', type=dict, required=False),
  103. Argument('group_perms', type=list, required=False)
  104. ).parse(request.body)
  105. if error is None:
  106. role = Role.objects.filter(pk=form.pop('id')).first()
  107. if not role:
  108. return json_response(error='未找到指定角色')
  109. if form.page_perms is not None:
  110. role.page_perms = json.dumps(form.page_perms)
  111. if form.deploy_perms is not None:
  112. role.deploy_perms = json.dumps(form.deploy_perms)
  113. if form.group_perms is not None:
  114. role.group_perms = json.dumps(form.group_perms)
  115. role.user_set.update(token_expired=0)
  116. role.save()
  117. return json_response(error=error)
  118. def delete(self, request):
  119. form, error = JsonParser(
  120. Argument('id', type=int, help='参数错误')
  121. ).parse(request.GET)
  122. if error is None:
  123. role = Role.objects.get(pk=form.id)
  124. if role.user_set.exists():
  125. return json_response(error='已有用户使用了该角色,请解除关联后再尝试删除')
  126. role.delete()
  127. return json_response(error=error)
  128. class SelfView(View):
  129. def patch(self, request):
  130. form, error = JsonParser(
  131. Argument('old_password', required=False),
  132. Argument('new_password', required=False),
  133. Argument('nickname', required=False),
  134. ).parse(request.body, True)
  135. if error is None:
  136. if form.get('old_password') and form.get('new_password'):
  137. if request.user.type == 'ldap':
  138. return json_response(error='LDAP账户无法修改密码')
  139. if len(form.new_password) < 6:
  140. return json_response(error='请设置至少6位的新密码')
  141. if request.user.verify_password(form.old_password):
  142. request.user.password_hash = User.make_password(form.new_password)
  143. request.user.token_expired = 0
  144. request.user.save()
  145. else:
  146. return json_response(error='原密码错误,请重新输入')
  147. if form.get('nickname'):
  148. request.user.nickname = form.nickname
  149. request.user.save()
  150. return json_response(error=error)
  151. def login(request):
  152. form, error = JsonParser(
  153. Argument('username', help='请输入用户名'),
  154. Argument('password', help='请输入密码'),
  155. Argument('captcha', required=False),
  156. Argument('type', required=False)
  157. ).parse(request.body)
  158. if error is None:
  159. user = User.objects.filter(username=form.username, type=form.type).first()
  160. if user and not user.is_active:
  161. return json_response(error="账户已被系统禁用")
  162. if form.type == 'ldap':
  163. config = AppSetting.get_default('ldap_service')
  164. if not config:
  165. return json_response(error='请在系统设置中配置LDAP后再尝试通过该方式登录')
  166. ldap = LDAP(**config)
  167. is_success, message = ldap.valid_user(form.username, form.password)
  168. if is_success:
  169. if not user:
  170. user = User.objects.create(username=form.username, nickname=form.username, type=form.type)
  171. return handle_user_info(request, user, form.captcha)
  172. elif message:
  173. return json_response(error=message)
  174. else:
  175. if user and user.deleted_by is None:
  176. if user.verify_password(form.password):
  177. return handle_user_info(request, user, form.captcha)
  178. value = cache.get_or_set(form.username, 0, 86400)
  179. if value >= 3:
  180. if user and user.is_active:
  181. user.is_active = False
  182. user.save()
  183. return json_response(error='账户已被系统禁用')
  184. cache.set(form.username, value + 1, 86400)
  185. return json_response(error="用户名或密码错误,连续多次错误账户将会被禁用")
  186. return json_response(error=error)
  187. def handle_user_info(request, user, captcha):
  188. cache.delete(user.username)
  189. key = f'{user.username}:code'
  190. if captcha:
  191. code = cache.get(key)
  192. if not code:
  193. return json_response(error='验证码已失效,请重新获取')
  194. if code != captcha:
  195. ttl = cache.ttl(key)
  196. cache.expire(key, ttl - 100)
  197. return json_response(error='验证码错误')
  198. cache.delete(key)
  199. else:
  200. mfa = AppSetting.get_default('MFA', {'enable': False})
  201. if mfa['enable']:
  202. if not user.wx_token:
  203. return json_response(error='已启用登录双重认证,但您的账户未配置微信Token,请联系管理员')
  204. code = generate_random_str(6)
  205. send_login_wx_code(user.wx_token, code)
  206. cache.set(key, code, 300)
  207. return json_response({'required_mfa': True})
  208. x_real_ip = get_request_real_ip(request.headers)
  209. token_isvalid = user.access_token and len(user.access_token) == 32 and user.token_expired >= time.time()
  210. user.access_token = user.access_token if token_isvalid else uuid.uuid4().hex
  211. user.token_expired = time.time() + 8 * 60 * 60
  212. user.last_login = human_datetime()
  213. user.last_ip = x_real_ip
  214. user.save()
  215. History.objects.create(user=user, ip=x_real_ip)
  216. verify_ip = AppSetting.get_default('verify_ip', True)
  217. return json_response({
  218. 'id': user.id,
  219. 'access_token': user.access_token,
  220. 'nickname': user.nickname,
  221. 'is_supper': user.is_supper,
  222. 'has_real_ip': x_real_ip and ipaddress.ip_address(x_real_ip).is_global if verify_ip else True,
  223. 'permissions': [] if user.is_supper else user.page_perms
  224. })
  225. def logout(request):
  226. request.user.token_expired = 0
  227. request.user.save()
  228. return json_response()