Brak opisu

cases.py 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # IRIS Source Code
  2. # Copyright (C) 2021 - Airbus CyberSecurity (SAS)
  3. # ir@cyberactionlab.net
  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 uuid
  19. from datetime import datetime
  20. from flask_login import current_user
  21. from sqlalchemy import BigInteger
  22. from sqlalchemy import CheckConstraint
  23. from sqlalchemy import Boolean
  24. from sqlalchemy import Column
  25. from sqlalchemy import Date
  26. from sqlalchemy import DateTime
  27. from sqlalchemy import ForeignKey
  28. from sqlalchemy import Integer
  29. from sqlalchemy import String
  30. from sqlalchemy import Text
  31. from sqlalchemy import UniqueConstraint
  32. from sqlalchemy import text
  33. from sqlalchemy.dialects.postgresql import JSON
  34. from sqlalchemy.dialects.postgresql import JSONB
  35. from sqlalchemy.dialects.postgresql import UUID
  36. from sqlalchemy.orm import relationship
  37. from sqlalchemy.orm import backref
  38. from app import db
  39. from app.datamgmt.states import update_assets_state
  40. from app.datamgmt.states import update_evidences_state
  41. from app.datamgmt.states import update_ioc_state
  42. from app.datamgmt.states import update_notes_state
  43. from app.datamgmt.states import update_tasks_state
  44. from app.datamgmt.states import update_timeline_state
  45. from app.models.models import Client
  46. class Cases(db.Model):
  47. __tablename__ = 'cases'
  48. case_id = Column(BigInteger, primary_key=True)
  49. soc_id = Column(String(256))
  50. client_id = Column(ForeignKey('client.client_id'), nullable=False)
  51. name = Column(String(256))
  52. description = Column(Text)
  53. open_date = Column(Date)
  54. close_date = Column(Date)
  55. initial_date = Column(DateTime, nullable=False, server_default=text("now()"))
  56. closing_note = Column(Text)
  57. user_id = Column(ForeignKey('user.id'))
  58. owner_id = Column(ForeignKey('user.id'))
  59. status_id = Column(Integer, nullable=False, server_default=text("0"))
  60. state_id = Column(ForeignKey('case_state.state_id'), nullable=True)
  61. custom_attributes = Column(JSON)
  62. case_uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, server_default=text("gen_random_uuid()"),
  63. nullable=False)
  64. classification_id = Column(ForeignKey('case_classification.id'))
  65. reviewer_id = Column(ForeignKey('user.id'), nullable=True)
  66. review_status_id = Column(ForeignKey('review_status.id'), nullable=True)
  67. severity_id = Column(ForeignKey('severities.severity_id'), nullable=True)
  68. modification_history = Column(JSON)
  69. client = relationship('Client')
  70. user = relationship('User', foreign_keys=[user_id])
  71. owner = relationship('User', foreign_keys=[owner_id])
  72. classification = relationship('CaseClassification')
  73. reviewer = relationship('User', foreign_keys=[reviewer_id])
  74. severity = relationship('Severity')
  75. alerts = relationship('Alert', secondary="alert_case_association", back_populates='cases', viewonly=True)
  76. tags = relationship('Tags', secondary="case_tags", back_populates='cases')
  77. state = relationship('CaseState', back_populates='cases')
  78. review_status = relationship('ReviewStatus')
  79. def __init__(self,
  80. name=None,
  81. soc_id=None,
  82. client_id=None,
  83. description=None,
  84. user=None,
  85. custom_attributes=None,
  86. classification_id=None,
  87. state_id=None,
  88. severity_id=None
  89. ):
  90. self.name = name[:200] if name else None,
  91. self.soc_id = soc_id,
  92. self.client_id = client_id,
  93. self.description = description,
  94. self.user_id = current_user.id if current_user else user.id
  95. self.owner_id = self.user_id
  96. self.author = current_user.user if current_user else user.user
  97. self.description = description
  98. self.open_date = datetime.utcnow()
  99. self.close_date = None
  100. self.initial_date = datetime.utcnow()
  101. self.custom_attributes = custom_attributes
  102. self.case_uuid = uuid.uuid4()
  103. self.status_id = 0
  104. self.classification_id = classification_id
  105. self.state_id = state_id,
  106. self.severity_id = severity_id
  107. def save(self):
  108. """
  109. Save the current case in database
  110. :return:
  111. """
  112. # Inject self into db
  113. db.session.add(self)
  114. # Commit the changes
  115. db.session.commit()
  116. # Rename case with the ID
  117. self.name = "#{id} - {name}".format(id=self.case_id, name=self.name)
  118. # Create the states
  119. update_timeline_state(caseid=self.case_id, userid=self.user_id)
  120. update_tasks_state(caseid=self.case_id, userid=self.user_id)
  121. update_evidences_state(caseid=self.case_id, userid=self.user_id)
  122. update_ioc_state(caseid=self.case_id, userid=self.user_id)
  123. update_assets_state(caseid=self.case_id, userid=self.user_id)
  124. update_notes_state(caseid=self.case_id, userid=self.user_id)
  125. db.session.commit()
  126. return self
  127. def validate_on_build(self):
  128. """
  129. Execute an autocheck of the case metadata and validate it
  130. :return: True if valid else false; Tuple with errors
  131. """
  132. # TODO : Check if case name already exists
  133. res = Client.query\
  134. .with_entities(Client.client_id)\
  135. .filter(Client.client_id == self.client_id)\
  136. .first()
  137. self.client_id = res[0]
  138. return True, []
  139. class CaseTags(db.Model):
  140. __tablename__ = 'case_tags'
  141. __table_args__ = (
  142. UniqueConstraint('case_id', 'tag_id', name='case_tags_case_id_tag_id_key'),
  143. )
  144. case_id = Column(ForeignKey('cases.case_id'), primary_key=True, nullable=False)
  145. tag_id = Column(ForeignKey('tags.id'), primary_key=True, nullable=False, index=True)
  146. class CasesEvent(db.Model):
  147. __tablename__ = "cases_events"
  148. event_id = Column(BigInteger, primary_key=True)
  149. parent_event_id = Column(BigInteger, ForeignKey('cases_events.event_id'), nullable=True)
  150. event_uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, server_default=text("gen_random_uuid()"),
  151. nullable=False)
  152. case_id = Column(ForeignKey('cases.case_id'))
  153. event_title = Column(Text)
  154. event_source = Column(Text)
  155. event_content = Column(Text)
  156. event_raw = Column(Text)
  157. event_date = Column(DateTime)
  158. event_added = Column(DateTime)
  159. event_in_graph = Column(Boolean)
  160. event_in_summary = Column(Boolean)
  161. user_id = Column(ForeignKey('user.id'))
  162. modification_history = Column(JSONB)
  163. event_color = Column(Text)
  164. event_tags = Column(Text)
  165. event_tz = Column(Text)
  166. event_date_wtz = Column(DateTime)
  167. event_is_flagged = Column(Boolean, default=False)
  168. custom_attributes = Column(JSONB)
  169. case = relationship('Cases')
  170. user = relationship('User')
  171. category = relationship('EventCategory', secondary="case_events_category",
  172. primaryjoin="CasesEvent.event_id==CaseEventCategory.event_id",
  173. secondaryjoin="CaseEventCategory.category_id==EventCategory.id",
  174. viewonly=True)
  175. children = relationship("CasesEvent", backref=backref('parent', remote_side=[event_id]))
  176. __table_args__ = (
  177. CheckConstraint('event_id != parent_event_id', name='check_different_ids'),
  178. )
  179. class CaseState(db.Model):
  180. __tablename__ = 'case_state'
  181. state_id = Column(Integer, primary_key=True)
  182. state_name = Column(Text, nullable=False)
  183. state_description = Column(Text)
  184. protected = Column(Boolean, default=False)
  185. cases = relationship('Cases', back_populates='state')
  186. class CaseProtagonist(db.Model):
  187. __tablename__ = "case_protagonist"
  188. id = Column(Integer, primary_key=True)
  189. case_id = Column(ForeignKey('cases.case_id'))
  190. user_id = Column(ForeignKey('user.id'))
  191. name = Column(Text)
  192. contact = Column(Text)
  193. role = Column(Text)
  194. case = relationship('Cases')
  195. user = relationship('User')