暫無描述

case_tasks_routes.py 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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. from datetime import datetime
  19. import marshmallow
  20. from flask import Blueprint
  21. from flask import request
  22. from flask_login import current_user
  23. from app import db
  24. from app.blueprints.rest.case_comments import case_comment_update
  25. from app.blueprints.rest.endpoints import endpoint_deprecated
  26. from app.business.errors import BusinessProcessingError
  27. from app.business.tasks import tasks_delete
  28. from app.business.tasks import tasks_create
  29. from app.business.tasks import tasks_get
  30. from app.business.tasks import tasks_update
  31. from app.datamgmt.case.case_tasks_db import add_comment_to_task
  32. from app.datamgmt.case.case_tasks_db import delete_task_comment
  33. from app.datamgmt.case.case_tasks_db import get_case_task_comment
  34. from app.datamgmt.case.case_tasks_db import get_case_task_comments
  35. from app.datamgmt.case.case_tasks_db import get_task
  36. from app.datamgmt.case.case_tasks_db import get_tasks_status
  37. from app.datamgmt.case.case_tasks_db import get_tasks_with_assignees
  38. from app.datamgmt.case.case_tasks_db import update_task_status
  39. from app.datamgmt.states import get_tasks_state
  40. from app.iris_engine.module_handler.module_handler import call_modules_hook
  41. from app.iris_engine.utils.tracker import track_activity
  42. from app.models.authorization import CaseAccessLevel
  43. from app.schema.marshables import CaseTaskSchema
  44. from app.schema.marshables import CommentSchema
  45. from app.blueprints.access_controls import ac_requires_case_identifier
  46. from app.blueprints.access_controls import ac_api_requires
  47. from app.blueprints.responses import response_error
  48. from app.blueprints.responses import response_success
  49. case_tasks_rest_blueprint = Blueprint('case_tasks_rest', __name__)
  50. @case_tasks_rest_blueprint.route('/case/tasks/list', methods=['GET'])
  51. @endpoint_deprecated('GET', '/api/v2/cases/<int:case_identifier>/tasks')
  52. @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
  53. @ac_api_requires()
  54. def case_get_tasks(caseid: int):
  55. ct = get_tasks_with_assignees(caseid)
  56. if not ct:
  57. output = []
  58. else:
  59. output = ct
  60. ret = {
  61. "tasks_status": get_tasks_status(),
  62. "tasks": output,
  63. "state": get_tasks_state(caseid=caseid)
  64. }
  65. return response_success("", data=ret)
  66. @case_tasks_rest_blueprint.route('/case/tasks/state', methods=['GET'])
  67. @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
  68. @ac_api_requires()
  69. def case_get_tasks_state(caseid: int):
  70. os = get_tasks_state(caseid=caseid)
  71. if os:
  72. return response_success(data=os)
  73. return response_error('No tasks state for this case.')
  74. @case_tasks_rest_blueprint.route('/case/tasks/status/update/<int:cur_id>', methods=['POST'])
  75. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  76. @ac_api_requires()
  77. def case_task_status_update(cur_id: int, caseid: int):
  78. task = get_task(task_id=cur_id)
  79. if not task:
  80. return response_error("Invalid task ID for this case")
  81. if request.is_json:
  82. if update_task_status(request.json.get('task_status_id'), cur_id, caseid):
  83. task_schema = CaseTaskSchema()
  84. return response_success("Task status updated", data=task_schema.dump(task))
  85. return response_error("Invalid status")
  86. return response_error("Invalid request")
  87. @case_tasks_rest_blueprint.route('/case/tasks/add', methods=['POST'])
  88. @endpoint_deprecated('POST', '/api/v2/cases/<int:caseid>/tasks')
  89. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  90. @ac_api_requires()
  91. def deprecated_case_add_task(caseid: int):
  92. task_schema = CaseTaskSchema()
  93. try:
  94. msg, task = tasks_create(case_identifier=caseid,
  95. request_json=request.get_json())
  96. return response_success(msg, data=task_schema.dump(task))
  97. except BusinessProcessingError as e:
  98. return response_error(e.get_message(), data=e.get_data())
  99. @case_tasks_rest_blueprint.route('/case/tasks/<int:cur_id>', methods=['GET'])
  100. @endpoint_deprecated('GET', '/api/v2/cases/<int:case_identifier>/tasks/<int:cur_id>')
  101. @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
  102. @ac_api_requires()
  103. def deprecated_case_task_view(cur_id: int, caseid: int):
  104. task = get_task(task_id=cur_id)
  105. if not task:
  106. return response_error('Invalid task ID for this case')
  107. task_schema = CaseTaskSchema()
  108. return response_success(data=task_schema.dump(task))
  109. @case_tasks_rest_blueprint.route('/case/tasks/update/<int:cur_id>', methods=['POST'])
  110. @endpoint_deprecated('PUT', '/api/v2/cases/<int:case_identifier>/tasks/<int:identifier>')
  111. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  112. @ac_api_requires()
  113. def deprecated_case_edit_task(cur_id: int, caseid: int):
  114. try:
  115. task = get_task(task_id=cur_id)
  116. if not task:
  117. return response_error(msg='Invalid task ID for this case')
  118. task = tasks_update(task, request.get_json())
  119. task_schema = CaseTaskSchema()
  120. return response_success(msg='Task updated', data=task_schema.dump(task))
  121. except marshmallow.exceptions.ValidationError as e:
  122. return response_error(msg='Data error', data=e.messages)
  123. @case_tasks_rest_blueprint.route('/case/tasks/delete/<int:cur_id>', methods=['POST'])
  124. @endpoint_deprecated('DELETE', '/api/v2/cases/<int:case_identifier>/tasks/<int:cur_id>')
  125. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  126. @ac_api_requires()
  127. def deprecated_case_delete_task(cur_id: int, caseid: int):
  128. try:
  129. task = tasks_get(identifier=cur_id)
  130. tasks_delete(task)
  131. return response_success('Task deleted')
  132. except BusinessProcessingError as e:
  133. return response_error(msg=e.get_message(), data=e.get_data())
  134. @case_tasks_rest_blueprint.route('/case/tasks/<int:cur_id>/comments/list', methods=['GET'])
  135. @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
  136. @ac_api_requires()
  137. def case_comment_task_list(cur_id: int, caseid: int):
  138. task_comments = get_case_task_comments(task_id=cur_id)
  139. if task_comments is None:
  140. return response_error('Invalid task ID')
  141. return response_success(data=CommentSchema(many=True).dump(task_comments))
  142. @case_tasks_rest_blueprint.route('/case/tasks/<int:cur_id>/comments/add', methods=['POST'])
  143. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  144. @ac_api_requires()
  145. def case_comment_task_add(cur_id: int, caseid: int):
  146. try:
  147. task = get_task(task_id=cur_id)
  148. if not task:
  149. return response_error('Invalid task ID')
  150. comment_schema = CommentSchema()
  151. comment = comment_schema.load(request.get_json())
  152. comment.comment_case_id = caseid
  153. comment.comment_user_id = current_user.id
  154. comment.comment_date = datetime.now()
  155. comment.comment_update_date = datetime.now()
  156. db.session.add(comment)
  157. db.session.commit()
  158. add_comment_to_task(task.id, comment.comment_id)
  159. db.session.commit()
  160. hook_data = {
  161. "comment": comment_schema.dump(comment),
  162. "task": CaseTaskSchema().dump(task)
  163. }
  164. call_modules_hook('on_postload_task_commented', data=hook_data, caseid=caseid)
  165. track_activity(f"task \"{task.task_title}\" commented", caseid=caseid)
  166. return response_success("Task commented", data=comment_schema.dump(comment))
  167. except marshmallow.exceptions.ValidationError as e:
  168. return response_error(msg="Data error", data=e.normalized_messages())
  169. @case_tasks_rest_blueprint.route('/case/tasks/<int:cur_id>/comments/<int:com_id>', methods=['GET'])
  170. @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
  171. @ac_api_requires()
  172. def case_comment_task_get(cur_id: int, com_id: int, caseid: int):
  173. comment = get_case_task_comment(task_id=cur_id,
  174. comment_id=com_id)
  175. if not comment:
  176. return response_error("Invalid comment ID")
  177. return response_success(data=comment._asdict())
  178. @case_tasks_rest_blueprint.route('/case/tasks/<int:cur_id>/comments/<int:com_id>/edit', methods=['POST'])
  179. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  180. @ac_api_requires()
  181. def case_comment_task_edit(cur_id: int, com_id: int, caseid: int):
  182. return case_comment_update(comment_id=com_id, object_type='tasks', caseid=caseid)
  183. @case_tasks_rest_blueprint.route('/case/tasks/<int:cur_id>/comments/<int:com_id>/delete', methods=['POST'])
  184. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  185. @ac_api_requires()
  186. def case_comment_task_delete(cur_id: int, com_id: int, caseid: int):
  187. success, msg = delete_task_comment(task_id=cur_id, comment_id=com_id)
  188. if not success:
  189. return response_error(msg)
  190. call_modules_hook('on_postload_task_comment_delete', data=com_id, caseid=caseid)
  191. track_activity(f"comment {com_id} on task {cur_id} deleted", caseid=caseid)
  192. return response_success(msg)