Brak opisu

manage_users.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. # IRIS Source Code
  2. # Copyright (C) 2024 - DFIR-IRIS
  3. # contact@dfir-iris.org
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU Lesser General Public
  7. # License as published by the Free Software Foundation; either
  8. # version 3 of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. # Lesser General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Lesser General Public License
  16. # along with this program; if not, write to the Free Software Foundation,
  17. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. import secrets
  19. import marshmallow
  20. import traceback
  21. from flask import Blueprint
  22. from flask import request
  23. from flask_login import current_user
  24. from app import app
  25. from app import db
  26. from app.blueprints.rest.parsing import parse_comma_separated_identifiers
  27. from app.datamgmt.manage.manage_users_db import add_case_access_to_user
  28. from app.datamgmt.manage.manage_users_db import update_user_customers
  29. from app.datamgmt.manage.manage_users_db import get_filtered_users
  30. from app.datamgmt.manage.manage_users_db import create_user
  31. from app.datamgmt.manage.manage_users_db import delete_user
  32. from app.datamgmt.manage.manage_users_db import get_user
  33. from app.datamgmt.manage.manage_users_db import get_user_by_username
  34. from app.datamgmt.manage.manage_users_db import get_user_details
  35. from app.datamgmt.manage.manage_users_db import get_users_list
  36. from app.datamgmt.manage.manage_users_db import get_users_list_restricted
  37. from app.datamgmt.manage.manage_users_db import remove_cases_access_from_user
  38. from app.datamgmt.manage.manage_users_db import update_user
  39. from app.datamgmt.manage.manage_users_db import update_user_groups
  40. from app.iris_engine.utils.tracker import track_activity
  41. from app.models.authorization import Permissions
  42. from app.schema.marshables import UserSchema
  43. from app.schema.marshables import BasicUserSchema
  44. from app.schema.marshables import UserFullSchema
  45. from app.blueprints.access_controls import ac_api_requires
  46. from app.blueprints.access_controls import ac_api_return_access_denied
  47. from app.blueprints.responses import response_error
  48. from app.blueprints.responses import response_success
  49. from app.iris_engine.demo_builder import protect_demo_mode_user
  50. manage_users_rest_blueprint = Blueprint('manage_users_rest', __name__)
  51. log = app.logger
  52. @manage_users_rest_blueprint.route('/manage/users/list', methods=['GET'])
  53. @ac_api_requires(Permissions.server_administrator)
  54. def manage_users_list():
  55. users = get_users_list()
  56. return response_success('', data=users)
  57. @manage_users_rest_blueprint.route('/manage/users/filter', methods=['GET'])
  58. @ac_api_requires(Permissions.server_administrator)
  59. def manage_users_filter():
  60. page = request.args.get('page', 1, type=int)
  61. per_page = request.args.get('per_page', 10, type=int)
  62. user_ids_str = request.args.get('user_ids', None, type=str)
  63. sort = request.args.get('sort', 'desc', type=str)
  64. if user_ids_str:
  65. try:
  66. user_ids_str = parse_comma_separated_identifiers(user_ids_str)
  67. except ValueError:
  68. return response_error('Invalid user_ids parameter')
  69. customer_id = request.args.get('customer_id', None, type=str)
  70. user_name = request.args.get('user_name', None, type=str)
  71. user_login = request.args.get('user_login', None, type=str)
  72. filtered_users = get_filtered_users(user_ids=user_ids_str,
  73. user_name=user_name,
  74. user_login=user_login,
  75. customer_id=customer_id,
  76. page=page,
  77. per_page=per_page,
  78. sort=sort)
  79. if filtered_users is None:
  80. return response_error('Filtering error')
  81. users = {
  82. 'total': filtered_users.total,
  83. 'users': BasicUserSchema().dump(filtered_users.items, many=True),
  84. 'last_page': filtered_users.pages,
  85. 'current_page': filtered_users.page,
  86. 'next_page': filtered_users.next_num if filtered_users.has_next else None
  87. }
  88. return response_success(data=users)
  89. @manage_users_rest_blueprint.route('/manage/users/add', methods=['POST'])
  90. @ac_api_requires(Permissions.server_administrator)
  91. def add_user():
  92. try:
  93. # validate before saving
  94. user_schema = UserSchema()
  95. jsdata = request.get_json()
  96. jsdata['user_id'] = 0
  97. jsdata['active'] = jsdata.get('active', True)
  98. cuser = user_schema.load(jsdata, partial=True)
  99. user = create_user(user_name=cuser.name,
  100. user_login=cuser.user,
  101. user_email=cuser.email,
  102. user_password=cuser.password,
  103. user_active=jsdata.get('active'),
  104. user_is_service_account=cuser.is_service_account)
  105. udata = user_schema.dump(user)
  106. udata['user_api_key'] = user.api_key
  107. del udata['user_password']
  108. if cuser:
  109. track_activity("created user {}".format(user.user), ctx_less=True)
  110. return response_success("user created", data=udata)
  111. return response_error("Unable to create user for internal reasons")
  112. except marshmallow.exceptions.ValidationError as e:
  113. return response_error(msg="Data error", data=e.messages)
  114. @manage_users_rest_blueprint.route('/manage/users/<int:cur_id>', methods=['GET'])
  115. @ac_api_requires(Permissions.server_administrator)
  116. def view_user(cur_id):
  117. user = get_user_details(user_id=cur_id)
  118. if not user:
  119. return response_error("Invalid user ID")
  120. return response_success(data=user)
  121. @manage_users_rest_blueprint.route('/manage/users/<int:cur_id>/groups/update', methods=['POST'])
  122. @ac_api_requires(Permissions.server_administrator)
  123. def manage_user_group_(cur_id):
  124. if not request.is_json:
  125. return response_error("Invalid request")
  126. if not request.json.get('groups_membership'):
  127. return response_error("Invalid request")
  128. if type(request.json.get('groups_membership')) is not list:
  129. return response_error("Expected list of groups ID")
  130. user = get_user_details(cur_id)
  131. if not user:
  132. return response_error("Invalid user ID")
  133. update_user_groups(user_id=cur_id,
  134. groups=request.json.get('groups_membership'))
  135. track_activity(f"groups membership of user {cur_id} updated", ctx_less=True)
  136. return response_success("User groups updated", data=user)
  137. @manage_users_rest_blueprint.route('/manage/users/<int:cur_id>/customers/update', methods=['POST'])
  138. @ac_api_requires(Permissions.server_administrator)
  139. def manage_user_customers_(cur_id):
  140. if not request.is_json:
  141. return response_error("Invalid request")
  142. if not request.json.get('customers_membership'):
  143. return response_error("Invalid request")
  144. if type(request.json.get('customers_membership')) is not list:
  145. return response_error("Expected list of customers ID")
  146. user = get_user_details(cur_id)
  147. if not user:
  148. return response_error('Invalid user ID')
  149. update_user_customers(user_id=cur_id, customers=request.json.get('customers_membership'))
  150. track_activity(f"customers membership of user {cur_id} updated", ctx_less=True)
  151. return response_success("User customers updated", data=user)
  152. @manage_users_rest_blueprint.route('/manage/users/<int:cur_id>/cases-access/update', methods=['POST'])
  153. @ac_api_requires(Permissions.server_administrator)
  154. def manage_user_cac_add_case(cur_id):
  155. if not request.is_json:
  156. return response_error("Invalid request, expecting JSON")
  157. data = request.get_json()
  158. if not data:
  159. return response_error("Invalid request, expecting JSON")
  160. user = get_user(cur_id)
  161. if not user:
  162. return response_error("Invalid user ID")
  163. if not isinstance(data.get('access_level'), int):
  164. try:
  165. data['access_level'] = int(data.get('access_level'))
  166. except:
  167. return response_error("Expecting access_level as int")
  168. if not isinstance(data.get('cases_list'), list):
  169. return response_error("Expecting cases_list as list")
  170. user, logs = add_case_access_to_user(user, data.get('cases_list'), data.get('access_level'))
  171. if not user:
  172. return response_error(msg=logs)
  173. track_activity(f"case access level {data.get('access_level')} for case(s) {data.get('cases_list')} "
  174. f"set for user {user.user}", ctx_less=True)
  175. group = get_user_details(cur_id)
  176. return response_success(data=group)
  177. @manage_users_rest_blueprint.route('/manage/users/<int:cur_id>/cases-access/delete', methods=['POST'])
  178. @ac_api_requires(Permissions.server_administrator)
  179. def manage_user_cac_delete_cases(cur_id):
  180. user = get_user(cur_id)
  181. if not user:
  182. return response_error("Invalid user ID")
  183. if not request.is_json:
  184. return response_error("Invalid request")
  185. data = request.get_json()
  186. if not data:
  187. return response_error("Invalid request")
  188. if not isinstance(data.get('cases'), list):
  189. return response_error("Expecting cases as list")
  190. try:
  191. success, logs = remove_cases_access_from_user(user.id, data.get('cases'))
  192. db.session.commit()
  193. except Exception as e:
  194. log.error("Error while removing cases access from user: {}".format(e))
  195. log.error(traceback.format_exc())
  196. return response_error(msg=str(e))
  197. if success:
  198. track_activity(f"cases access for case(s) {data.get('cases')} deleted for user {user.user}",
  199. ctx_less=True)
  200. user = get_user_details(cur_id)
  201. return response_success(msg="User case access updated", data=user)
  202. return response_error(msg=logs)
  203. @manage_users_rest_blueprint.route('/manage/users/update/<int:cur_id>', methods=['POST'])
  204. @ac_api_requires(Permissions.server_administrator)
  205. def update_user_api(cur_id):
  206. try:
  207. user = get_user(cur_id)
  208. if not user:
  209. return response_error("Invalid user ID for this case")
  210. if protect_demo_mode_user(user):
  211. return ac_api_return_access_denied()
  212. # validate before saving
  213. user_schema = UserSchema()
  214. jsdata = request.get_json()
  215. jsdata['user_id'] = cur_id
  216. cuser = user_schema.load(jsdata, instance=user, partial=True)
  217. update_user(password=jsdata.get('user_password'),
  218. user=user)
  219. db.session.commit()
  220. if cuser:
  221. track_activity("updated user {}".format(user.user), ctx_less=True)
  222. return response_success("User updated", data=user_schema.dump(user))
  223. return response_error("Unable to update user for internal reasons")
  224. except marshmallow.exceptions.ValidationError as e:
  225. return response_error(msg="Data error", data=e.messages)
  226. @manage_users_rest_blueprint.route('/manage/users/deactivate/<int:cur_id>', methods=['GET'])
  227. @ac_api_requires(Permissions.server_administrator)
  228. def deactivate_user_api(cur_id):
  229. user = get_user(cur_id)
  230. if not user:
  231. return response_error("Invalid user ID for this case")
  232. if protect_demo_mode_user(user):
  233. return ac_api_return_access_denied()
  234. if current_user.id == cur_id:
  235. return response_error('We do not recommend deactivating yourself for obvious reasons')
  236. user.active = False
  237. db.session.commit()
  238. user_schema = UserSchema()
  239. track_activity(f"user {user.user} deactivated", ctx_less=True)
  240. return response_success("User deactivated", data=user_schema.dump(user))
  241. @manage_users_rest_blueprint.route('/manage/users/activate/<int:cur_id>', methods=['GET'])
  242. @ac_api_requires(Permissions.server_administrator)
  243. def activate_user_api(cur_id):
  244. user = get_user(cur_id)
  245. if not user:
  246. return response_error("Invalid user ID for this case")
  247. if protect_demo_mode_user(user):
  248. return ac_api_return_access_denied()
  249. user.active = True
  250. db.session.commit()
  251. user_schema = UserSchema()
  252. track_activity(f"user {user.user} activated", ctx_less=True)
  253. return response_success("User activated", data=user_schema.dump(user))
  254. @manage_users_rest_blueprint.route('/manage/users/renew-api-key/<int:cur_id>', methods=['POST'])
  255. @ac_api_requires(Permissions.server_administrator)
  256. def renew_user_api_key(cur_id):
  257. user = get_user(cur_id)
  258. if not user:
  259. return response_error("Invalid user ID for this case")
  260. if protect_demo_mode_user(user):
  261. return ac_api_return_access_denied()
  262. user.api_key = secrets.token_urlsafe(nbytes=64)
  263. db.session.commit()
  264. user_schema = UserFullSchema()
  265. track_activity(f"API key of user {user.user} renewed", ctx_less=True)
  266. return response_success(f"API key of user {user.user} renewed", data=user_schema.dump(user))
  267. @manage_users_rest_blueprint.route('/manage/users/delete/<int:cur_id>', methods=['POST'])
  268. @ac_api_requires(Permissions.server_administrator)
  269. def view_delete_user(cur_id):
  270. try:
  271. user = get_user(cur_id)
  272. if not user:
  273. return response_error("Invalid user ID")
  274. if protect_demo_mode_user(user):
  275. return ac_api_return_access_denied()
  276. if user.active is True:
  277. track_activity(message="tried to delete active user ID {}".format(cur_id), ctx_less=True)
  278. return response_error("Cannot delete active user")
  279. delete_user(user.id)
  280. track_activity(message="deleted user ID {}".format(cur_id), ctx_less=True)
  281. return response_success("Deleted user ID {}".format(cur_id))
  282. except Exception as e:
  283. print(e)
  284. db.session.rollback()
  285. track_activity(message="tried to delete active user ID {}".format(cur_id), ctx_less=True)
  286. return response_error("Cannot delete active user")
  287. # Unrestricted section - non admin available
  288. @manage_users_rest_blueprint.route('/manage/users/lookup/id/<int:cur_id>', methods=['GET'])
  289. @ac_api_requires()
  290. def exists_user_restricted(cur_id):
  291. user = get_user(cur_id)
  292. if not user:
  293. return response_error("Invalid user ID")
  294. output = {
  295. "user_login": user.user,
  296. "user_id": user.id,
  297. "user_name": user.name
  298. }
  299. return response_success(data=output)
  300. @manage_users_rest_blueprint.route('/manage/users/lookup/login/<string:login>', methods=['GET'])
  301. @ac_api_requires()
  302. def lookup_name_restricted(login):
  303. user = get_user_by_username(login)
  304. if not user:
  305. return response_error("Invalid login")
  306. output = {
  307. "user_login": user.user,
  308. "user_id": user.id,
  309. "user_uuid": user.uuid,
  310. "user_name": user.name,
  311. "user_email": user.email,
  312. "user_active": user.active
  313. }
  314. return response_success(data=output)
  315. @manage_users_rest_blueprint.route('/manage/users/restricted/list', methods=['GET'])
  316. @ac_api_requires()
  317. def manage_users_list_restricted():
  318. users = get_users_list_restricted()
  319. return response_success('', data=users)