Nav apraksta

dashboard_routes.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 marshmallow
  19. from datetime import datetime
  20. from datetime import timedelta
  21. from oic.oauth2.exception import GrantError
  22. from flask import Blueprint
  23. from flask import session
  24. from flask import request
  25. from flask import redirect
  26. from flask_login import current_user
  27. from flask_login import logout_user
  28. from app import db
  29. from app import app
  30. from app import oidc_client
  31. from app.blueprints.rest.endpoints import endpoint_deprecated
  32. from app.datamgmt.dashboard.dashboard_db import get_global_task, list_user_cases, list_user_reviews
  33. from app.datamgmt.dashboard.dashboard_db import get_tasks_status
  34. from app.datamgmt.dashboard.dashboard_db import list_global_tasks
  35. from app.datamgmt.dashboard.dashboard_db import list_user_tasks
  36. from app.forms import CaseGlobalTaskForm
  37. from app.iris_engine.module_handler.module_handler import call_modules_hook
  38. from app.iris_engine.utils.tracker import track_activity
  39. from app.models.authorization import User
  40. from app.models.cases import Cases
  41. from app.models.models import CaseTasks
  42. from app.models.models import GlobalTasks
  43. from app.models.models import TaskStatus
  44. from app.schema.marshables import CaseTaskSchema
  45. from app.schema.marshables import CaseDetailsSchema
  46. from app.schema.marshables import GlobalTasksSchema
  47. from app.blueprints.access_controls import ac_requires_case_identifier
  48. from app.blueprints.access_controls import ac_api_requires
  49. from app.blueprints.responses import response_error
  50. from app.blueprints.responses import response_success
  51. from app.blueprints.access_controls import is_authentication_oidc
  52. from app.blueprints.access_controls import not_authenticated_redirection_url
  53. log = app.logger
  54. dashboard_rest_blueprint = Blueprint(
  55. 'dashboard_rest',
  56. __name__,
  57. template_folder='templates'
  58. )
  59. # Logout user
  60. @dashboard_rest_blueprint.route('/logout')
  61. def logout():
  62. """
  63. Logout function. Erase its session and redirect to index i.e login
  64. :return: Page
  65. """
  66. if session['current_case']:
  67. current_user.ctx_case = session['current_case']['case_id']
  68. current_user.ctx_human_case = session['current_case']['case_name']
  69. db.session.commit()
  70. if is_authentication_oidc():
  71. if oidc_client.provider_info.get("end_session_endpoint"):
  72. try:
  73. logout_request = oidc_client.construct_EndSessionRequest(
  74. state=session["oidc_state"])
  75. logout_url = logout_request.request(
  76. oidc_client.provider_info["end_session_endpoint"])
  77. track_activity("user '{}' is been logged-out".format(
  78. current_user.user), ctx_less=True, display_in_ui=False)
  79. logout_user()
  80. session.clear()
  81. return redirect(logout_url)
  82. except GrantError:
  83. track_activity(
  84. f"no oidc session found for user '{current_user.user}', skipping oidc provider logout and continuing to logout local user",
  85. ctx_less=True,
  86. display_in_ui=False
  87. )
  88. track_activity("user '{}' is been logged-out".format(current_user.user),
  89. ctx_less=True, display_in_ui=False)
  90. logout_user()
  91. session.clear()
  92. return redirect(not_authenticated_redirection_url('/'))
  93. @dashboard_rest_blueprint.route('/dashboard/case_charts', methods=['GET'])
  94. @ac_api_requires()
  95. def get_cases_charts():
  96. """
  97. Get case charts
  98. :return: JSON
  99. """
  100. res = Cases.query.with_entities(
  101. Cases.open_date
  102. ).filter(
  103. Cases.open_date > (datetime.utcnow() - timedelta(days=365))
  104. ).order_by(
  105. Cases.open_date
  106. ).all()
  107. retr = [[], []]
  108. rk = {}
  109. for case in res:
  110. month = "{}/{}/{}".format(case.open_date.day,
  111. case.open_date.month, case.open_date.year)
  112. if month in rk:
  113. rk[month] += 1
  114. else:
  115. rk[month] = 1
  116. retr = [list(rk.keys()), list(rk.values())]
  117. return response_success("", retr)
  118. @dashboard_rest_blueprint.route('/global/tasks/list', methods=['GET'])
  119. @ac_api_requires()
  120. def get_gtasks():
  121. tasks_list = list_global_tasks()
  122. if tasks_list:
  123. output = [c._asdict() for c in tasks_list]
  124. else:
  125. output = []
  126. ret = {
  127. "tasks_status": get_tasks_status(),
  128. "tasks": output
  129. }
  130. return response_success("", data=ret)
  131. @dashboard_rest_blueprint.route('/global/tasks/<int:cur_id>', methods=['GET'])
  132. @ac_api_requires()
  133. def view_gtask(cur_id):
  134. task = get_global_task(task_id=cur_id)
  135. if not task:
  136. return response_error(f'Global task ID {cur_id} not found')
  137. return response_success("", data=task._asdict())
  138. @dashboard_rest_blueprint.route('/user/tasks/status/update', methods=['POST'])
  139. @ac_api_requires()
  140. @ac_requires_case_identifier()
  141. def utask_statusupdate(caseid):
  142. jsdata = request.get_json()
  143. if not jsdata:
  144. return response_error("Invalid request")
  145. jsdata = request.get_json()
  146. if not jsdata:
  147. return response_error("Invalid request")
  148. case_id = jsdata.get('case_id') if jsdata.get('case_id') else caseid
  149. task_id = jsdata.get('task_id')
  150. task = CaseTasks.query.filter(
  151. CaseTasks.id == task_id, CaseTasks.task_case_id == case_id).first()
  152. if not task:
  153. return response_error(f"Invalid case task ID {task_id} for case {case_id}")
  154. status_id = jsdata.get('task_status_id')
  155. status = TaskStatus.query.filter(TaskStatus.id == status_id).first()
  156. if not status:
  157. return response_error(f"Invalid task status ID {status_id}")
  158. task.task_status_id = status_id
  159. try:
  160. db.session.commit()
  161. except Exception as e:
  162. return response_error(f"Unable to update task. Error {e}")
  163. task_schema = CaseTaskSchema()
  164. return response_success("Updated", data=task_schema.dump(task))
  165. @dashboard_rest_blueprint.route('/global/tasks/add', methods=['POST'])
  166. @ac_api_requires()
  167. @ac_requires_case_identifier()
  168. def add_gtask(caseid):
  169. try:
  170. gtask_schema = GlobalTasksSchema()
  171. request_data = call_modules_hook(
  172. 'on_preload_global_task_create', data=request.get_json(), caseid=caseid)
  173. gtask = gtask_schema.load(request_data)
  174. except marshmallow.exceptions.ValidationError as e:
  175. return response_error(msg="Data error", data=e.messages)
  176. gtask.task_userid_update = current_user.id
  177. gtask.task_open_date = datetime.utcnow()
  178. gtask.task_last_update = datetime.utcnow()
  179. gtask.task_last_update = datetime.utcnow()
  180. try:
  181. db.session.add(gtask)
  182. db.session.commit()
  183. except Exception as e:
  184. return response_error(msg="Data error", data=e.__str__())
  185. gtask = call_modules_hook(
  186. 'on_postload_global_task_create', data=gtask, caseid=caseid)
  187. track_activity("created new global task \'{}\'".format(
  188. gtask.task_title), caseid=caseid)
  189. return response_success('Task added', data=gtask_schema.dump(gtask))
  190. @dashboard_rest_blueprint.route('/global/tasks/update/<int:cur_id>', methods=['POST'])
  191. @ac_api_requires()
  192. @ac_requires_case_identifier()
  193. def edit_gtask(cur_id, caseid):
  194. form = CaseGlobalTaskForm()
  195. task = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
  196. form.task_assignee_id.choices = [(user.id, user.name) for user in User.query.filter(
  197. User.active == True).order_by(User.name).all()]
  198. form.task_status_id.choices = [(a.id, a.status_name)
  199. for a in get_tasks_status()]
  200. if not task:
  201. return response_error(msg="Data error", data="Invalid task ID")
  202. try:
  203. gtask_schema = GlobalTasksSchema()
  204. request_data = call_modules_hook('on_preload_global_task_update', data=request.get_json(),
  205. caseid=caseid)
  206. gtask = gtask_schema.load(request_data, instance=task)
  207. gtask.task_userid_update = current_user.id
  208. gtask.task_last_update = datetime.utcnow()
  209. db.session.commit()
  210. gtask = call_modules_hook(
  211. 'on_postload_global_task_update', data=gtask, caseid=caseid)
  212. except marshmallow.exceptions.ValidationError as e:
  213. return response_error(msg="Data error", data=e.messages)
  214. track_activity("updated global task {} (status {})".format(
  215. task.task_title, task.task_status_id), caseid=caseid)
  216. return response_success('Task updated', data=gtask_schema.dump(gtask))
  217. @dashboard_rest_blueprint.route('/global/tasks/delete/<int:cur_id>', methods=['POST'])
  218. @ac_api_requires()
  219. @ac_requires_case_identifier()
  220. def gtask_delete(cur_id, caseid):
  221. call_modules_hook('on_preload_global_task_delete',
  222. data=cur_id, caseid=caseid)
  223. if not cur_id:
  224. return response_error("Missing parameter")
  225. data = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
  226. if not data:
  227. return response_error("Invalid global task ID")
  228. GlobalTasks.query.filter(GlobalTasks.id == cur_id).delete()
  229. db.session.commit()
  230. call_modules_hook('on_postload_global_task_delete',
  231. data=request.get_json(), caseid=caseid)
  232. track_activity("deleted global task ID {}".format(cur_id), caseid=caseid)
  233. return response_success("Task deleted")
  234. @dashboard_rest_blueprint.route('/user/cases/list', methods=['GET'])
  235. @endpoint_deprecated('GET', '/api/v2/cases?case_owner_id=<user_id>')
  236. @ac_api_requires()
  237. def list_own_cases():
  238. cases = list_user_cases(
  239. request.args.get('show_closed', 'false', type=str).lower() == 'true'
  240. )
  241. return response_success("", data=CaseDetailsSchema(many=True).dump(cases))
  242. @dashboard_rest_blueprint.route('/user/tasks/list', methods=['GET'])
  243. @ac_api_requires()
  244. def get_utasks():
  245. ct = list_user_tasks()
  246. if ct:
  247. output = [c._asdict() for c in ct]
  248. else:
  249. output = []
  250. ret = {
  251. "tasks_status": get_tasks_status(),
  252. "tasks": output
  253. }
  254. return response_success("", data=ret)
  255. @dashboard_rest_blueprint.route('/user/reviews/list', methods=['GET'])
  256. @ac_api_requires()
  257. def get_reviews():
  258. ct = list_user_reviews()
  259. if ct:
  260. output = [c._asdict() for c in ct]
  261. else:
  262. output = []
  263. return response_success("", data=output)