first sync
Some checks failed
Deployment Verification / deploy-and-test (push) Failing after 29s

This commit is contained in:
2025-03-04 07:59:21 +01:00
parent 9cdcf486b6
commit 506716e703
1450 changed files with 577316 additions and 62 deletions

View File

@ -0,0 +1,399 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import datetime
from flask_login import current_user
from sqlalchemy import and_
from sqlalchemy import func
from app import db, app
from app.datamgmt.states import update_assets_state
from app.models import AnalysisStatus, CaseStatus
from app.models import AssetComments
from app.models import AssetsType
from app.models import CaseAssets
from app.models import CaseEventsAssets
from app.models import Cases
from app.models import Comments
from app.models import CompromiseStatus
from app.models import Ioc
from app.models import IocAssetLink
from app.models import IocLink
from app.models import IocType
from app.models.authorization import User
log = app.logger
def create_asset(asset, caseid, user_id):
asset.date_added = datetime.datetime.utcnow()
asset.date_update = datetime.datetime.utcnow()
asset.case_id = caseid
asset.user_id = user_id
db.session.add(asset)
update_assets_state(caseid=caseid, userid=user_id)
db.session.commit()
return asset
def get_assets(caseid):
assets = CaseAssets.query.with_entities(
CaseAssets.asset_id,
CaseAssets.asset_uuid,
CaseAssets.asset_name,
AssetsType.asset_name.label('asset_type'),
AssetsType.asset_icon_compromised,
AssetsType.asset_icon_not_compromised,
CaseAssets.asset_description,
CaseAssets.asset_domain,
CaseAssets.asset_compromise_status_id,
CaseAssets.asset_ip,
CaseAssets.asset_type_id,
AnalysisStatus.name.label('analysis_status'),
CaseAssets.analysis_status_id,
CaseAssets.asset_tags
).filter(
CaseAssets.case_id == caseid,
).join(
CaseAssets.asset_type, CaseAssets.analysis_status
).all()
return assets
def get_assets_name(caseid):
assets_names = CaseAssets.query.with_entities(
CaseAssets.asset_name
).filter(
CaseAssets.case_id == caseid
).all()
return assets_names
def get_asset(asset_id, caseid):
asset = CaseAssets.query.filter(
CaseAssets.asset_id == asset_id,
CaseAssets.case_id == caseid
).first()
return asset
def update_asset(asset_name, asset_description, asset_ip, asset_info, asset_domain,
asset_compromise_status_id, asset_type, asset_id, caseid, analysis_status, asset_tags):
asset = get_asset(asset_id, caseid)
asset.asset_name = asset_name
asset.asset_description = asset_description
asset.asset_ip = asset_ip
asset.asset_info = asset_info
asset.asset_domain = asset_domain
asset.asset_compromise_status_id = asset_compromise_status_id
asset.asset_type_id = asset_type
asset.analysis_status_id = analysis_status
asset.asset_tags = asset_tags
update_assets_state(caseid=caseid)
db.session.commit()
def delete_asset(asset_id, caseid):
case_asset = get_asset(asset_id, caseid)
if case_asset is None:
return
if case_asset.case_id and case_asset.alerts is not None:
CaseEventsAssets.query.filter(
case_asset.asset_id == CaseEventsAssets.asset_id
).delete()
case_asset.case_id = None
db.session.commit()
return
with db.session.begin_nested():
delete_ioc_asset_link(asset_id)
# Delete the relevant records from the CaseEventsAssets table
CaseEventsAssets.query.filter(
CaseEventsAssets.case_id == caseid,
CaseEventsAssets.asset_id == asset_id
).delete()
# Delete the relevant records from the AssetComments table
com_ids = AssetComments.query.with_entities(
AssetComments.comment_id
).filter(
AssetComments.comment_asset_id == asset_id,
).all()
com_ids = [c.comment_id for c in com_ids]
AssetComments.query.filter(AssetComments.comment_id.in_(com_ids)).delete()
Comments.query.filter(
Comments.comment_id.in_(com_ids)
).delete()
# Directly delete the relevant records from the CaseAssets table
CaseAssets.query.filter(
CaseAssets.asset_id == asset_id,
CaseAssets.case_id == caseid
).delete()
update_assets_state(caseid=caseid)
def get_assets_types():
assets_types = [(c.asset_id, c.asset_name) for c
in AssetsType.query.with_entities(AssetsType.asset_name,
AssetsType.asset_id).order_by(AssetsType.asset_name)
]
return assets_types
def get_unspecified_analysis_status_id():
"""
Get the id of the 'Unspecified' analysis status
"""
analysis_status = AnalysisStatus.query.filter(
AnalysisStatus.name == 'Unspecified'
).first()
return analysis_status.id if analysis_status else None
def get_analysis_status_list():
analysis_status = [(c.id, c.name) for c in AnalysisStatus.query.with_entities(
AnalysisStatus.id,
AnalysisStatus.name
)]
return analysis_status
def get_compromise_status_list():
return [(e.value, e.name.replace('_', ' ').capitalize()) for e in CompromiseStatus]
def get_compromise_status_dict():
return [{'value': e.value, 'name': e.name.replace('_', ' ').capitalize()} for e in CompromiseStatus]
def get_case_outcome_status_dict():
return [{'value': e.value, 'name': e.name.replace('_', ' ').capitalize()} for e in CaseStatus]
def get_asset_type_id(asset_type_name):
assets_type_id = AssetsType.query.with_entities(
AssetsType.asset_id
).filter(
func.lower(AssetsType.asset_name) == asset_type_name
).first()
return assets_type_id
def get_assets_ioc_links(caseid):
ioc_links_req = IocAssetLink.query.with_entities(
Ioc.ioc_id,
Ioc.ioc_value,
IocAssetLink.asset_id
).filter(
Ioc.ioc_id == IocAssetLink.ioc_id,
IocLink.case_id == caseid,
IocLink.ioc_id == Ioc.ioc_id
).all()
return ioc_links_req
def get_similar_assets(asset_name, asset_type_id, caseid, customer_id, cases_limitation):
linked_assets = CaseAssets.query.with_entities(
Cases.name.label('case_name'),
Cases.open_date.label('case_open_date'),
CaseAssets.asset_description,
CaseAssets.asset_compromise_status_id,
CaseAssets.asset_id,
CaseAssets.case_id
).filter(
Cases.client_id == customer_id,
CaseAssets.case_id != caseid
).filter(
CaseAssets.asset_name == asset_name,
CaseAssets.asset_type_id == asset_type_id,
Cases.case_id.in_(cases_limitation)
).join(CaseAssets.case).all()
return (lasset._asdict() for lasset in linked_assets)
def delete_ioc_asset_link(asset_id):
IocAssetLink.query.filter(
IocAssetLink.asset_id == asset_id
).delete()
def get_linked_iocs_from_asset(asset_id):
iocs = IocAssetLink.query.with_entities(
Ioc.ioc_id,
Ioc.ioc_value
).filter(
IocAssetLink.asset_id == asset_id,
Ioc.ioc_id == IocAssetLink.ioc_id
).all()
return iocs
def set_ioc_links(ioc_list, asset_id):
if ioc_list is None:
return False, "Empty IOC list"
# Reset IOC list
delete_ioc_asset_link(asset_id)
for ioc in ioc_list:
ial = IocAssetLink()
ial.asset_id = asset_id
ial.ioc_id = ioc
db.session.add(ial)
try:
db.session.commit()
except Exception as e:
db.session.rollback()
log.exception(e)
return True, e.__str__()
return False, ""
def get_linked_iocs_id_from_asset(asset_id):
iocs = IocAssetLink.query.with_entities(
IocAssetLink.ioc_id
).filter(
IocAssetLink.asset_id == asset_id
).all()
return iocs
def get_linked_iocs_finfo_from_asset(asset_id):
iocs = IocAssetLink.query.with_entities(
Ioc.ioc_id,
Ioc.ioc_value,
Ioc.ioc_tags,
Ioc.ioc_type_id,
IocType.type_name,
Ioc.ioc_description,
Ioc.ioc_tlp_id
).filter(and_(
IocAssetLink.asset_id == asset_id,
IocAssetLink.ioc_id == Ioc.ioc_id
)).join(Ioc.ioc_type).all()
return iocs
def get_case_asset_comments(asset_id):
return Comments.query.filter(
AssetComments.comment_asset_id == asset_id
).with_entities(
Comments
).join(AssetComments,
Comments.comment_id == AssetComments.comment_id
).order_by(
Comments.comment_date.asc()
).all()
def add_comment_to_asset(asset_id, comment_id):
ec = AssetComments()
ec.comment_asset_id = asset_id
ec.comment_id = comment_id
db.session.add(ec)
db.session.commit()
def get_case_assets_comments_count(asset_id):
return AssetComments.query.filter(
AssetComments.comment_asset_id.in_(asset_id)
).with_entities(
AssetComments.comment_asset_id,
AssetComments.comment_id
).group_by(
AssetComments.comment_asset_id,
AssetComments.comment_id
).all()
def get_case_asset_comment(asset_id, comment_id):
return AssetComments.query.filter(
AssetComments.comment_asset_id == asset_id,
AssetComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
User.name,
User.user
).join(
AssetComments.comment,
Comments.user
).first()
def delete_asset_comment(asset_id, comment_id, case_id):
comment = Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_user_id == current_user.id
).first()
if not comment:
return False, "You are not allowed to delete this comment"
AssetComments.query.filter(
AssetComments.comment_asset_id == asset_id,
AssetComments.comment_id == comment_id
).delete()
db.session.delete(comment)
db.session.commit()
return True, "Comment deleted"
def get_asset_by_name(asset_name, caseid):
asset = CaseAssets.query.filter(
CaseAssets.asset_name == asset_name,
CaseAssets.case_id == caseid
).first()
return asset

View File

@ -0,0 +1,13 @@
from app.models import Comments
def get_case_comment(comment_id, caseid):
if caseid is None:
return Comments.query.filter(
Comments.comment_id == comment_id
).first()
else:
return Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_case_id == caseid
).first()

View File

@ -0,0 +1,211 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import datetime
import binascii
from sqlalchemy import and_
from app import db
from app.models import Tags
from app.models.cases import CaseProtagonist
from app.models.cases import CaseTags
from app.models.cases import Cases
from app.models.models import CaseTemplateReport, ReviewStatus
from app.models.models import Client
from app.models.models import Languages
from app.models.models import ReportType
from app.models.authorization import User
def get_case_summary(caseid):
case_summary = Cases.query.filter(
Cases.case_id == caseid
).with_entities(
Cases.name.label('case_name'),
Cases.open_date.label('case_open'),
User.name.label('user'),
Client.name.label('customer')
).join(
Cases.user, Cases.client
).first()
return case_summary
def get_case(caseid):
return Cases.query.filter(Cases.case_id == caseid).first()
def case_exists(caseid):
return Cases.query.filter(Cases.case_id == caseid).count()
def get_case_client_id(caseid):
client_id = Cases.query.with_entities(
Client.client_id
).filter(
Cases.case_id == caseid
).join(Cases.client).first()
return client_id.client_id
def case_get_desc(caseid):
case_desc = Cases.query.with_entities(
Cases.description
).filter(
Cases.case_id == caseid
).first()
return case_desc
def case_get_desc_crc(caseid):
partial_case = case_get_desc(caseid)
if partial_case:
desc = partial_case.description
if not desc:
desc = ""
desc_crc32 = binascii.crc32(desc.encode('utf-8'))
else:
desc = None
desc_crc32 = None
return desc_crc32, desc
def case_set_desc_crc(desc, caseid):
lcase = get_case(caseid)
if lcase:
if not desc:
desc = ""
lcase.description = desc
db.session.commit()
return True
return False
def get_case_report_template():
reports = CaseTemplateReport.query.with_entities(
CaseTemplateReport.id,
CaseTemplateReport.name,
Languages.name,
CaseTemplateReport.description
).join(
CaseTemplateReport.language,
CaseTemplateReport.report_type
).filter(
ReportType.name == "Investigation"
).all()
return reports
def save_case_tags(tags, case):
if tags is None:
return
case.tags.clear()
for tag in tags.split(','):
tag = tag.strip()
if tag:
tg = Tags.query.filter_by(tag_title=tag).first()
if tg is None:
tg = Tags(tag_title=tag)
tg.save()
case.tags.append(tg)
db.session.commit()
def get_case_tags(case_id):
case = Cases.query.get(case_id)
if case:
return [tag.tag_title for tag in case.tags]
return []
def get_activities_report_template():
reports = CaseTemplateReport.query.with_entities(
CaseTemplateReport.id,
CaseTemplateReport.name,
Languages.name,
CaseTemplateReport.description
).join(
CaseTemplateReport.language,
CaseTemplateReport.report_type
).filter(
ReportType.name == "Activities"
).all()
return reports
def case_name_exists(case_name, client_name):
res = Cases.query.with_entities(
Cases.name, Client.name
).filter(and_(
Cases.name == case_name,
Client.name == client_name
)).join(
Cases.client
).first()
return True if res else False
def register_case_protagonists(case_id, protagonists):
if protagonists is None:
return
CaseProtagonist.query.filter(
CaseProtagonist.case_id == case_id
).delete()
for protagonist in protagonists:
for key in ['role', 'name']:
if not protagonist.get(key):
continue
cp = CaseProtagonist()
cp.case_id = case_id
cp.role = protagonist.get('role')
cp.name = protagonist.get('name')
cp.contact = protagonist.get('contact')
db.session.add(cp)
db.session.commit()
def get_review_id_from_name(review_name):
status = ReviewStatus.query.filter(ReviewStatus.status_name == review_name).first()
if status:
return status.id
return None

View File

@ -0,0 +1,402 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from flask_login import current_user
from sqlalchemy import and_
from app import db
from app.datamgmt.states import update_timeline_state
from app.models import AssetsType
from app.models import CaseAssets
from app.models import CaseEventCategory
from app.models import CaseEventsAssets
from app.models import CaseEventsIoc
from app.models import CasesEvent
from app.models import Comments
from app.models import EventCategory
from app.models import EventComments
from app.models import Ioc
from app.models import IocAssetLink
from app.models import IocLink
from app.models import IocType
from app.models.authorization import User
def get_case_events_assets_graph(caseid):
events = CaseEventsAssets.query.with_entities(
CaseEventsAssets.event_id,
CasesEvent.event_uuid,
CasesEvent.event_title,
CaseAssets.asset_name,
CaseAssets.asset_id,
AssetsType.asset_name.label('type_name'),
AssetsType.asset_icon_not_compromised,
AssetsType.asset_icon_compromised,
CasesEvent.event_color,
CaseAssets.asset_compromise_status_id,
CaseAssets.asset_description,
CaseAssets.asset_ip,
CasesEvent.event_date,
CasesEvent.event_tags
).filter(and_(
CaseEventsAssets.case_id == caseid,
CasesEvent.event_in_graph == True
)).join(
CaseEventsAssets.event,
CaseEventsAssets.asset,
CaseAssets.asset_type,
).all()
return events
def get_case_events_ioc_graph(caseid):
events = CaseEventsIoc.query.with_entities(
CaseEventsIoc.event_id,
CasesEvent.event_uuid,
CasesEvent.event_title,
CasesEvent.event_date,
Ioc.ioc_id,
Ioc.ioc_value,
Ioc.ioc_description,
IocType.type_name
).filter(and_(
CaseEventsIoc.case_id == caseid,
CasesEvent.event_in_graph == True
)).join(
CaseEventsIoc.event,
CaseEventsIoc.ioc,
Ioc.ioc_type,
).all()
return events
def get_events_categories():
return EventCategory.query.with_entities(
EventCategory.id,
EventCategory.name
).all()
def get_default_cat():
cat = EventCategory.query.with_entities(
EventCategory.id,
EventCategory.name
).filter(
EventCategory.name == "Unspecified"
).first()
return [cat._asdict()]
def get_case_event(event_id, caseid):
return CasesEvent.query.filter(
CasesEvent.event_id == event_id,
CasesEvent.case_id == caseid
).first()
def get_case_event_comments(event_id, caseid):
return Comments.query.filter(
EventComments.comment_event_id == event_id
).join(
EventComments,
Comments.comment_id == EventComments.comment_id
).order_by(
Comments.comment_date.asc()
).all()
def get_case_events_comments_count(events_list):
return EventComments.query.filter(
EventComments.comment_event_id.in_(events_list)
).with_entities(
EventComments.comment_event_id,
EventComments.comment_id
).group_by(
EventComments.comment_event_id,
EventComments.comment_id
).all()
def get_case_event_comment(event_id, comment_id, caseid):
return EventComments.query.filter(
EventComments.comment_event_id == event_id,
EventComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
User.name,
User.user
).join(
EventComments.comment,
Comments.user
).first()
def delete_event_comment(event_id, comment_id):
comment = Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_user_id == current_user.id
).first()
if not comment:
return False, "You are not allowed to delete this comment"
EventComments.query.filter(
EventComments.comment_event_id == event_id,
EventComments.comment_id == comment_id
).delete()
db.session.delete(comment)
db.session.commit()
return True, "Comment deleted"
def add_comment_to_event(event_id, comment_id):
ec = EventComments()
ec.comment_event_id = event_id
ec.comment_id = comment_id
db.session.add(ec)
db.session.commit()
def delete_event_category(event_id):
CaseEventCategory.query.filter(
CaseEventCategory.event_id == event_id
).delete()
def get_event_category(event_id):
cec = CaseEventCategory.query.filter(
CaseEventCategory.event_id == event_id
).first()
return cec
def save_event_category(event_id, category_id):
CaseEventCategory.query.filter(
CaseEventCategory.event_id == event_id
).delete()
cec = CaseEventCategory()
cec.event_id = event_id
cec.category_id = category_id
db.session.add(cec)
db.session.commit()
def get_event_assets_ids(event_id, caseid):
assets_list = CaseEventsAssets.query.with_entities(
CaseEventsAssets.asset_id
).filter(
CaseEventsAssets.event_id == event_id,
CaseEventsAssets.case_id == caseid
).all()
return [x[0] for x in assets_list]
def get_event_iocs_ids(event_id, caseid):
iocs_list = CaseEventsIoc.query.with_entities(
CaseEventsIoc.ioc_id
).filter(
CaseEventsIoc.event_id == event_id,
CaseEventsIoc.case_id == caseid
).all()
return [x[0] for x in iocs_list]
def update_event_assets(event_id, caseid, assets_list, iocs_list, sync_iocs_assets):
CaseEventsAssets.query.filter(
CaseEventsAssets.event_id == event_id,
CaseEventsAssets.case_id == caseid
).delete()
valid_assets = CaseAssets.query.with_entities(
CaseAssets.asset_id
).filter(
CaseAssets.asset_id.in_(assets_list),
CaseAssets.case_id == caseid
).all()
for asset in valid_assets:
try:
cea = CaseEventsAssets()
cea.asset_id = int(asset.asset_id)
cea.event_id = event_id
cea.case_id = caseid
db.session.add(cea)
if sync_iocs_assets:
for ioc in iocs_list:
link = IocAssetLink.query.filter(
IocAssetLink.asset_id == int(asset.asset_id),
IocAssetLink.ioc_id == int(ioc)
).first()
if link is None:
ial = IocAssetLink()
ial.asset_id = int(asset.asset_id)
ial.ioc_id = int(ioc)
db.session.add(ial)
except Exception as e:
return False, str(e)
db.session.commit()
return True, ''
def update_event_iocs(event_id, caseid, iocs_list):
CaseEventsIoc.query.filter(
CaseEventsIoc.event_id == event_id,
CaseEventsIoc.case_id == caseid
).delete()
valid_iocs = IocLink.query.with_entities(
IocLink.ioc_id
).filter(
IocLink.ioc_id.in_(iocs_list),
IocLink.case_id == caseid
).all()
for ioc in valid_iocs:
try:
cea = CaseEventsIoc()
cea.ioc_id = int(ioc.ioc_id)
cea.event_id = event_id
cea.case_id = caseid
db.session.add(cea)
except Exception as e:
return False, str(e)
db.session.commit()
return True, ''
def get_case_assets_for_tm(caseid):
"""
Return a list of all assets linked to the current case
:return: Tuple of assets
"""
assets = [{'asset_name': '', 'asset_id': '0'}]
assets_list = CaseAssets.query.with_entities(
CaseAssets.asset_name,
CaseAssets.asset_id,
AssetsType.asset_name.label('type')
).filter(
CaseAssets.case_id == caseid
).join(CaseAssets.asset_type).order_by(CaseAssets.asset_name).all()
for asset in assets_list:
assets.append({
'asset_name': "{} ({})".format(asset.asset_name, asset.type),
'asset_id': asset.asset_id
})
return assets
def get_case_iocs_for_tm(caseid):
iocs = [{'ioc_value': '', 'ioc_id': '0'}]
iocs_list = Ioc.query.with_entities(
Ioc.ioc_value,
Ioc.ioc_id
).filter(
IocLink.case_id == caseid
).join(
IocLink.ioc
).order_by(
Ioc.ioc_value
).all()
for ioc in iocs_list:
iocs.append({
'ioc_value': "{}".format(ioc.ioc_value),
'ioc_id': ioc.ioc_id
})
return iocs
def delete_event(event, caseid):
delete_event_category(event.event_id)
CaseEventsAssets.query.filter(
CaseEventsAssets.event_id == event.event_id,
CaseEventsAssets.case_id == caseid
).delete()
CaseEventsIoc.query.filter(
CaseEventsIoc.event_id == event.event_id,
CaseEventsIoc.case_id == caseid
).delete()
com_ids = EventComments.query.with_entities(
EventComments.comment_id
).filter(
EventComments.comment_event_id == event.event_id
).all()
com_ids = [c.comment_id for c in com_ids]
EventComments.query.filter(EventComments.comment_id.in_(com_ids)).delete()
Comments.query.filter(Comments.comment_id.in_(com_ids)).delete()
db.session.commit()
db.session.delete(event)
update_timeline_state(caseid=caseid)
db.session.commit()
def get_category_by_name(cat_name):
return EventCategory.query.filter(
EventCategory.name == cat_name,
).first()
def get_default_category():
return EventCategory.query.with_entities(
EventCategory.id,
EventCategory.name
).filter(
EventCategory.name == "Unspecified"
).first()

View File

@ -0,0 +1,356 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from flask_login import current_user
from sqlalchemy import and_
from app import db
from app.datamgmt.states import update_ioc_state
from app.iris_engine.access_control.utils import ac_get_fast_user_cases_access
from app.models import CaseEventsIoc
from app.models import Cases
from app.models import Client
from app.models import Comments
from app.models import Ioc
from app.models import IocAssetLink
from app.models import IocComments
from app.models import IocLink
from app.models import IocType
from app.models import Tlp
from app.models.authorization import User
def get_iocs(caseid):
iocs = IocLink.query.with_entities(
Ioc.ioc_value,
Ioc.ioc_id,
Ioc.ioc_uuid
).filter(
IocLink.case_id == caseid,
IocLink.ioc_id == Ioc.ioc_id
).all()
return iocs
def get_ioc(ioc_id, caseid=None):
if caseid:
return IocLink.query.with_entities(
Ioc
).filter(and_(
Ioc.ioc_id == ioc_id,
IocLink.case_id == caseid
)).join(
IocLink.ioc
).first()
return Ioc.query.filter(Ioc.ioc_id == ioc_id).first()
def update_ioc(ioc_type, ioc_tags, ioc_value, ioc_description, ioc_tlp, userid, ioc_id):
ioc = get_ioc(ioc_id)
if ioc:
ioc.ioc_type = ioc_type
ioc.ioc_tags = ioc_tags
ioc.ioc_value = ioc_value
ioc.ioc_description = ioc_description
ioc.ioc_tlp_id = ioc_tlp
ioc.user_id = userid
db.session.commit()
else:
return False
def delete_ioc(ioc, caseid):
with db.session.begin_nested():
IocLink.query.filter(
and_(
IocLink.ioc_id == ioc.ioc_id,
IocLink.case_id == caseid
)
).delete()
res = IocLink.query.filter(
IocLink.ioc_id == ioc.ioc_id,
).all()
if res:
return False
IocAssetLink.query.filter(
IocAssetLink.ioc_id == ioc.ioc_id
).delete()
CaseEventsIoc.query.filter(
CaseEventsIoc.ioc_id == ioc.ioc_id
).delete()
com_ids = IocComments.query.with_entities(
IocComments.comment_id
).filter(
IocComments.comment_ioc_id == ioc.ioc_id
).all()
com_ids = [c.comment_id for c in com_ids]
IocComments.query.filter(IocComments.comment_id.in_(com_ids)).delete()
Comments.query.filter(Comments.comment_id.in_(com_ids)).delete()
db.session.delete(ioc)
update_ioc_state(caseid=caseid)
return True
def get_detailed_iocs(caseid):
detailed_iocs = IocLink.query.with_entities(
Ioc.ioc_id,
Ioc.ioc_uuid,
Ioc.ioc_value,
Ioc.ioc_type_id,
IocType.type_name.label('ioc_type'),
Ioc.ioc_type_id,
Ioc.ioc_description,
Ioc.ioc_tags,
Ioc.ioc_misp,
Tlp.tlp_name,
Tlp.tlp_bscolor,
Ioc.ioc_tlp_id
).filter(
and_(IocLink.case_id == caseid,
IocLink.ioc_id == Ioc.ioc_id)
).join(IocLink.ioc,
Ioc.tlp,
Ioc.ioc_type
).order_by(IocType.type_name).all()
return detailed_iocs
def get_ioc_links(ioc_id, caseid):
search_condition = and_(Cases.case_id.in_([]))
user_search_limitations = ac_get_fast_user_cases_access(current_user.id)
if user_search_limitations:
search_condition = and_(Cases.case_id.in_(user_search_limitations))
ioc_link = IocLink.query.with_entities(
Cases.case_id,
Cases.name.label('case_name'),
Client.name.label('client_name')
).filter(and_(
IocLink.ioc_id == ioc_id,
IocLink.case_id != caseid,
search_condition)
).join(IocLink.case, Cases.client).all()
return ioc_link
def find_ioc(ioc_value, ioc_type_id):
ioc = Ioc.query.filter(Ioc.ioc_value == ioc_value,
Ioc.ioc_type_id == ioc_type_id).first()
return ioc
def add_ioc(ioc, user_id, caseid):
if not ioc:
return None, False
ioc.user_id = user_id
db_ioc = find_ioc(ioc.ioc_value, ioc.ioc_type_id)
if not db_ioc:
db.session.add(ioc)
update_ioc_state(caseid=caseid)
db.session.commit()
return ioc, False
else:
# IoC already exists
return db_ioc, True
def find_ioc_link(ioc_id, caseid):
db_link = IocLink.query.filter(
IocLink.case_id == caseid,
IocLink.ioc_id == ioc_id
).first()
return db_link
def add_ioc_link(ioc_id, caseid):
db_link = find_ioc_link(ioc_id, caseid)
if db_link:
# Link already exists
return True
else:
link = IocLink()
link.case_id = caseid
link.ioc_id = ioc_id
db.session.add(link)
db.session.commit()
return False
def get_ioc_types_list():
ioc_types = IocType.query.with_entities(
IocType.type_id,
IocType.type_name,
IocType.type_description,
IocType.type_taxonomy,
IocType.type_validation_regex,
IocType.type_validation_expect,
).all()
l_types = [row._asdict() for row in ioc_types]
return l_types
def add_ioc_type(name:str, description:str, taxonomy:str):
ioct = IocType(type_name=name,
type_description=description,
type_taxonomy=taxonomy
)
db.session.add(ioct)
db.session.commit()
return ioct
def check_ioc_type_id(type_id: int):
type_id = IocType.query.filter(
IocType.type_id == type_id
).first()
return type_id
def get_ioc_type_id(type_name: str):
type_id = IocType.query.filter(
IocType.type_name == type_name
).first()
return type_id if type_id else None
def get_tlps():
return [(tlp.tlp_id, tlp.tlp_name) for tlp in Tlp.query.all()]
def get_tlps_dict():
tlpDict = {}
for tlp in Tlp.query.all():
tlpDict[tlp.tlp_name]=tlp.tlp_id
return tlpDict
def get_case_ioc_comments(ioc_id):
return Comments.query.filter(
IocComments.comment_ioc_id == ioc_id
).with_entities(
Comments
).join(
IocComments,
Comments.comment_id == IocComments.comment_id
).order_by(
Comments.comment_date.asc()
).all()
def add_comment_to_ioc(ioc_id, comment_id):
ec = IocComments()
ec.comment_ioc_id = ioc_id
ec.comment_id = comment_id
db.session.add(ec)
db.session.commit()
def get_case_iocs_comments_count(iocs_list):
return IocComments.query.filter(
IocComments.comment_ioc_id.in_(iocs_list)
).with_entities(
IocComments.comment_ioc_id,
IocComments.comment_id
).group_by(
IocComments.comment_ioc_id,
IocComments.comment_id
).all()
def get_case_ioc_comment(ioc_id, comment_id):
return IocComments.query.filter(
IocComments.comment_ioc_id == ioc_id,
IocComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
User.name,
User.user
).join(
IocComments.comment,
Comments.user
).first()
def delete_ioc_comment(ioc_id, comment_id):
comment = Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_user_id == current_user.id
).first()
if not comment:
return False, "You are not allowed to delete this comment"
IocComments.query.filter(
IocComments.comment_ioc_id == ioc_id,
IocComments.comment_id == comment_id
).delete()
db.session.delete(comment)
db.session.commit()
return True, "Comment deleted"
def get_ioc_by_value(ioc_value, caseid=None):
if caseid:
return IocLink.query.with_entities(
Ioc
).filter(and_(
Ioc.ioc_value == ioc_value,
IocLink.case_id == caseid
)).join(
IocLink.ioc
).first()
return Ioc.query.filter(Ioc.ioc_value == ioc_value).first()

View File

@ -0,0 +1,374 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from flask_login import current_user
from sqlalchemy import and_
from app import db
from app.datamgmt.manage.manage_attribute_db import get_default_custom_attributes
from app.datamgmt.states import update_notes_state
from app.models import Comments
from app.models import Notes
from app.models import NotesComments
from app.models import NotesGroup
from app.models import NotesGroupLink
from app.models.authorization import User
def get_note(note_id, caseid=None):
note = Notes.query.with_entities(
Notes.note_id,
Notes.note_uuid,
Notes.note_title,
Notes.note_content,
Notes.note_creationdate,
Notes.note_lastupdate,
NotesGroupLink.group_id,
NotesGroup.group_title,
NotesGroup.group_uuid,
Notes.custom_attributes,
Notes.note_case_id
).filter(and_(
Notes.note_id == note_id,
Notes.note_case_id == caseid
)).join(
NotesGroupLink.note,
NotesGroupLink.note_group
).first()
return note
def get_note_raw(note_id, caseid):
note = Notes.query.filter(
Notes.note_case_id == caseid,
Notes.note_id == note_id
).first()
return note
def delete_note(note_id, caseid):
with db.session.begin_nested():
NotesGroupLink.query.filter(and_(
NotesGroupLink.note_id == note_id,
NotesGroupLink.case_id == caseid
)).delete()
com_ids = NotesComments.query.with_entities(
NotesComments.comment_id
).filter(
NotesComments.comment_note_id == note_id
).all()
com_ids = [c.comment_id for c in com_ids]
NotesComments.query.filter(NotesComments.comment_id.in_(com_ids)).delete()
Comments.query.filter(Comments.comment_id.in_(com_ids)).delete()
Notes.query.filter(Notes.note_id == note_id).delete()
update_notes_state(caseid=caseid)
def update_note(note_content, note_title, update_date, user_id, note_id, caseid):
note = get_note_raw(note_id, caseid=caseid)
if note:
note.note_content = note_content
note.note_title = note_title
note.note_lastupdate = update_date
note.note_user = user_id
db.session.commit()
return note
else:
return None
def add_note(note_title, creation_date, user_id, caseid, group_id, note_content=""):
note = Notes()
note.note_title = note_title
note.note_creationdate = note.note_lastupdate = creation_date
note.note_content = note_content
note.note_case_id = caseid
note.note_user = user_id
note.custom_attributes = get_default_custom_attributes('note')
db.session.add(note)
update_notes_state(caseid=caseid, userid=user_id)
db.session.commit()
if note.note_id:
ngl = NotesGroupLink()
ngl.note_id = note.note_id
ngl.group_id = group_id
ngl.case_id = caseid
db.session.add(ngl)
db.session.commit()
return note
else:
return None
def get_groups_short(caseid):
groups_short = NotesGroup.query.with_entities(
NotesGroup.group_id,
NotesGroup.group_uuid,
NotesGroup.group_title
).filter(
NotesGroup.group_case_id == caseid
).order_by(
NotesGroup.group_id
).all()
return groups_short
def get_notes_from_group(caseid, group_id):
notes = NotesGroupLink.query.with_entities(
Notes.note_id,
Notes.note_uuid,
Notes.note_title,
User.user,
Notes.note_lastupdate
).filter(
NotesGroupLink.case_id == caseid,
NotesGroupLink.group_id == group_id,
).join(
NotesGroupLink.note,
Notes.user
).order_by(
Notes.note_id
).all()
return notes
def get_groups_detail(caseid):
groups = NotesGroupLink.query.with_entities(
NotesGroup.group_id,
NotesGroup.group_uuid,
NotesGroup.group_title,
Notes.note_id,
Notes.note_uuid,
Notes.note_title,
User.user,
Notes.note_lastupdate
).filter(
NotesGroupLink.case_id == caseid,
).join(
NotesGroupLink.note,
NotesGroupLink.note_group,
Notes.user
).group_by(
NotesGroup.group_id,
Notes.note_id,
User.user
).all()
return groups
def get_group_details(group_id, caseid):
group_l = NotesGroup.query.with_entities(
NotesGroup.group_id,
NotesGroup.group_uuid,
NotesGroup.group_title,
NotesGroup.group_creationdate,
NotesGroup.group_lastupdate
).filter(
NotesGroup.group_case_id == caseid
).filter(
NotesGroup.group_id == group_id
).first()
group = None
if group_l:
group = group_l._asdict()
group['notes'] = [note._asdict() for note in get_notes_from_group(caseid=caseid, group_id=group_id)]
return group
def add_note_group(group_title, caseid, userid, creationdate):
ng = NotesGroup()
ng.group_title = group_title
ng.group_case_id = caseid
ng.group_user = userid
ng.group_creationdate = creationdate
ng.group_lastupdate = creationdate
db.session.add(ng)
update_notes_state(caseid=caseid, userid=userid)
db.session.commit()
if group_title == '':
ng.group_title = "New notes group"
db.session.commit()
return ng
def delete_note_group(group_id, caseid):
ngl = NotesGroupLink.query.with_entities(
NotesGroupLink.note_id
).filter(
NotesGroupLink.group_id == group_id,
NotesGroupLink.case_id == caseid
).all()
if not ngl:
group = NotesGroup.query.filter(and_(
NotesGroup.group_id == group_id,
NotesGroup.group_case_id == caseid
)).first()
if not group:
return False
db.session.delete(group)
update_notes_state(caseid=caseid)
db.session.commit()
return True
to_delete = [row.note_id for row in ngl]
NotesGroupLink.query.filter(
NotesGroupLink.group_id == group_id,
NotesGroupLink.case_id == caseid
).delete()
db.session.commit()
for nid in to_delete:
Notes.query.filter(Notes.note_id == nid).delete()
NotesGroup.query.filter(and_(
NotesGroup.group_id == group_id,
NotesGroup.group_case_id == caseid
)).delete()
update_notes_state(caseid=caseid)
db.session.commit()
return True
def update_note_group(group_title, group_id, caseid):
ng = NotesGroup.query.filter(and_(
NotesGroup.group_id == group_id,
NotesGroup.group_case_id == caseid
)).first()
if ng:
ng.group_title = group_title
update_notes_state(caseid=caseid)
db.session.commit()
return ng
else:
return None
def find_pattern_in_notes(pattern, caseid):
notes = Notes.query.filter(
Notes.note_content.like(pattern),
Notes.note_case_id == caseid
).with_entities(
Notes.note_id,
Notes.note_title
).all()
return notes
def get_case_note_comments(note_id):
return Comments.query.filter(
NotesComments.comment_note_id == note_id
).join(
NotesComments,
Comments.comment_id == NotesComments.comment_id
).order_by(
Comments.comment_date.asc()
).all()
def add_comment_to_note(note_id, comment_id):
ec = NotesComments()
ec.comment_note_id = note_id
ec.comment_id = comment_id
db.session.add(ec)
db.session.commit()
def get_case_notes_comments_count(notes_list):
return NotesComments.query.filter(
NotesComments.comment_note_id.in_(notes_list)
).with_entities(
NotesComments.comment_note_id,
NotesComments.comment_id
).group_by(
NotesComments.comment_note_id,
NotesComments.comment_id
).all()
def get_case_note_comment(note_id, comment_id):
return NotesComments.query.filter(
NotesComments.comment_note_id == note_id,
NotesComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
User.name,
User.user
).join(
NotesComments.comment,
Comments.user
).first()
def delete_note_comment(note_id, comment_id):
comment = Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_user_id == current_user.id
).first()
if not comment:
return False, "You are not allowed to delete this comment"
NotesComments.query.filter(
NotesComments.comment_note_id == note_id,
NotesComments.comment_id == comment_id
).delete()
db.session.delete(comment)
db.session.commit()
return True, "Comment deleted"

View File

@ -0,0 +1,174 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import datetime
from flask_login import current_user
from sqlalchemy import and_
from sqlalchemy import desc
from app import db
from app.datamgmt.manage.manage_attribute_db import get_default_custom_attributes
from app.datamgmt.states import update_evidences_state
from app.models import CaseReceivedFile
from app.models import Comments
from app.models import EvidencesComments
from app.models.authorization import User
def get_rfiles(caseid):
crf = CaseReceivedFile.query.with_entities(
CaseReceivedFile.id,
CaseReceivedFile.file_uuid,
CaseReceivedFile.filename,
CaseReceivedFile.date_added,
CaseReceivedFile.file_hash,
CaseReceivedFile.file_description,
CaseReceivedFile.file_size,
User.name.label('username')
).filter(
CaseReceivedFile.case_id == caseid
).join(CaseReceivedFile.user).order_by(desc(CaseReceivedFile.date_added)).all()
return crf
def add_rfile(evidence, caseid, user_id):
evidence.date_added = datetime.datetime.now()
evidence.case_id = caseid
evidence.user_id = user_id
evidence.custom_attributes = get_default_custom_attributes('evidence')
db.session.add(evidence)
update_evidences_state(caseid=caseid, userid=user_id)
db.session.commit()
return evidence
def get_rfile(rfile_id, caseid):
return CaseReceivedFile.query.filter(
CaseReceivedFile.id == rfile_id,
CaseReceivedFile.case_id == caseid
).first()
def update_rfile(evidence, user_id, caseid):
evidence.user_id = user_id
update_evidences_state(caseid=caseid, userid=user_id)
db.session.commit()
return evidence
def delete_rfile(rfile_id, caseid):
with db.session.begin_nested():
com_ids = EvidencesComments.query.with_entities(
EvidencesComments.comment_id
).filter(
EvidencesComments.comment_evidence_id == rfile_id
).all()
com_ids = [c.comment_id for c in com_ids]
EvidencesComments.query.filter(EvidencesComments.comment_id.in_(com_ids)).delete()
Comments.query.filter(Comments.comment_id.in_(com_ids)).delete()
CaseReceivedFile.query.filter(and_(
CaseReceivedFile.id == rfile_id,
CaseReceivedFile.case_id == caseid,
)).delete()
update_evidences_state(caseid=caseid)
db.session.commit()
def get_case_evidence_comments(evidence_id):
return Comments.query.filter(
EvidencesComments.comment_evidence_id == evidence_id
).join(
EvidencesComments,
Comments.comment_id == EvidencesComments.comment_id
).order_by(
Comments.comment_date.asc()
).all()
def add_comment_to_evidence(evidence_id, comment_id):
ec = EvidencesComments()
ec.comment_evidence_id = evidence_id
ec.comment_id = comment_id
db.session.add(ec)
db.session.commit()
def get_case_evidence_comments_count(evidences_list):
return EvidencesComments.query.filter(
EvidencesComments.comment_evidence_id.in_(evidences_list)
).with_entities(
EvidencesComments.comment_evidence_id,
EvidencesComments.comment_id
).group_by(
EvidencesComments.comment_evidence_id,
EvidencesComments.comment_id
).all()
def get_case_evidence_comment(evidence_id, comment_id):
return EvidencesComments.query.filter(
EvidencesComments.comment_evidence_id == evidence_id,
EvidencesComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
User.name,
User.user
).join(
EvidencesComments.comment,
Comments.user
).first()
def delete_evidence_comment(evidence_id, comment_id):
comment = Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_user_id == current_user.id
).first()
if not comment:
return False, "You are not allowed to delete this comment"
EvidencesComments.query.filter(
EvidencesComments.comment_evidence_id == evidence_id,
EvidencesComments.comment_id == comment_id
).delete()
db.session.delete(comment)
db.session.commit()
return True, "Comment deleted"

View File

@ -0,0 +1,335 @@
#!/usr/bin/env python3
#
# IRIS Source Code
# Copyright (C) 2021 - Airbus CyberSecurity (SAS)
# ir@cyberactionlab.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from datetime import datetime
from flask_login import current_user
from sqlalchemy import desc, and_
from app import db
from app.datamgmt.manage.manage_attribute_db import get_default_custom_attributes
from app.datamgmt.manage.manage_users_db import get_users_list_restricted_from_case
from app.datamgmt.states import update_tasks_state
from app.models import CaseTasks, TaskAssignee
from app.models import Cases
from app.models import Comments
from app.models import TaskComments
from app.models import TaskStatus
from app.models.authorization import User
def get_tasks_status():
return TaskStatus.query.all()
def get_tasks(caseid):
return CaseTasks.query.with_entities(
CaseTasks.id.label("task_id"),
CaseTasks.task_uuid,
CaseTasks.task_title,
CaseTasks.task_description,
CaseTasks.task_open_date,
CaseTasks.task_tags,
CaseTasks.task_status_id,
TaskStatus.status_name,
TaskStatus.status_bscolor
).filter(
CaseTasks.task_case_id == caseid
).join(
CaseTasks.status
).order_by(
desc(TaskStatus.status_name)
).all()
def get_tasks_with_assignees(caseid):
tasks = get_tasks(caseid)
if not tasks:
return None
tasks = [c._asdict() for c in tasks]
task_with_assignees = []
for task in tasks:
task_id = task['task_id']
get_assignee_list = TaskAssignee.query.with_entities(
TaskAssignee.task_id,
User.user,
User.id,
User.name
).join(
TaskAssignee.user
).filter(
TaskAssignee.task_id == task_id
).all()
assignee_list = {}
for member in get_assignee_list:
if member.task_id not in assignee_list:
assignee_list[member.task_id] = [{
'user': member.user,
'name': member.name,
'id': member.id
}]
else:
assignee_list[member.task_id].append({
'user': member.user,
'name': member.name,
'id': member.id
})
task['task_assignees'] = assignee_list.get(task['task_id'], [])
task_with_assignees.append(task)
return task_with_assignees
def get_task(task_id, caseid):
return CaseTasks.query.filter(CaseTasks.id == task_id, CaseTasks.task_case_id == caseid).first()
def get_task_with_assignees(task_id: int, case_id: int):
"""
Returns a task with its assignees
Args:
task_id (int): Task ID
case_id (int): Case ID
Returns:
dict: Task with its assignees
"""
task = get_task(
task_id=task_id,
caseid=case_id
)
if not task:
return None
get_assignee_list = TaskAssignee.query.with_entities(
TaskAssignee.task_id,
User.user,
User.id,
User.name
).join(
TaskAssignee.user
).filter(
TaskAssignee.task_id == task_id
).all()
assignee_list = {}
for member in get_assignee_list:
if member.task_id not in assignee_list:
assignee_list[member.task_id] = [{
'user': member.user,
'name': member.name,
'id': member.id
}]
else:
assignee_list[member.task_id].append({
'user': member.user,
'name': member.name,
'id': member.id
})
setattr(task, 'task_assignees', assignee_list.get(task.id, []))
return task
def update_task_status(task_status, task_id, caseid):
task = get_task(task_id, caseid)
if task:
try:
task.task_status_id = task_status
update_tasks_state(caseid=caseid)
db.session.commit()
return True
except:
return False
else:
return False
def update_task_assignees(task, task_assignee_list, caseid):
if not task:
return None
cur_assignee_list = TaskAssignee.query.with_entities(
TaskAssignee.user_id
).filter(TaskAssignee.task_id == task.id).all()
# Some formatting
set_cur_assignees = set([assignee[0] for assignee in cur_assignee_list])
set_assignees = set(int(assignee) for assignee in task_assignee_list)
assignees_to_add = set_assignees - set_cur_assignees
assignees_to_remove = set_cur_assignees - set_assignees
allowed_users = [u.get('user_id') for u in get_users_list_restricted_from_case(caseid)]
for uid in assignees_to_add:
if uid not in allowed_users:
continue
user = User.query.filter(User.id == uid).first()
if user:
ta = TaskAssignee()
ta.task_id = task.id
ta.user_id = user.id
db.session.add(ta)
for uid in assignees_to_remove:
TaskAssignee.query.filter(
and_(TaskAssignee.task_id == task.id,
TaskAssignee.user_id == uid)
).delete()
db.session.commit()
return task
def add_task(task, assignee_id_list, user_id, caseid):
now = datetime.now()
task.task_case_id = caseid
task.task_userid_open = user_id
task.task_userid_update = user_id
task.task_open_date = now
task.task_last_update = now
task.custom_attributes = task.custom_attributes if task.custom_attributes else get_default_custom_attributes('task')
db.session.add(task)
update_tasks_state(caseid=caseid)
db.session.commit()
update_task_status(task.task_status_id, task.id, caseid)
update_task_assignees(task, assignee_id_list, caseid)
return task
def get_case_task_comments(task_id):
return Comments.query.filter(
TaskComments.comment_task_id == task_id
).join(
TaskComments,
Comments.comment_id == TaskComments.comment_id
).order_by(
Comments.comment_date.asc()
).all()
def add_comment_to_task(task_id, comment_id):
ec = TaskComments()
ec.comment_task_id = task_id
ec.comment_id = comment_id
db.session.add(ec)
db.session.commit()
def get_case_tasks_comments_count(tasks_list):
return TaskComments.query.filter(
TaskComments.comment_task_id.in_(tasks_list)
).with_entities(
TaskComments.comment_task_id,
TaskComments.comment_id
).group_by(
TaskComments.comment_task_id,
TaskComments.comment_id
).all()
def get_case_task_comment(task_id, comment_id):
return TaskComments.query.filter(
TaskComments.comment_task_id == task_id,
TaskComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
User.name,
User.user
).join(
TaskComments.comment,
Comments.user
).first()
def delete_task(task_id):
with db.session.begin_nested():
TaskAssignee.query.filter(
TaskAssignee.task_id == task_id
).delete()
com_ids = TaskComments.query.with_entities(
TaskComments.comment_id
).filter(
TaskComments.comment_task_id == task_id
).all()
com_ids = [c.comment_id for c in com_ids]
TaskComments.query.filter(TaskComments.comment_id.in_(com_ids)).delete()
Comments.query.filter(Comments.comment_id.in_(com_ids)).delete()
CaseTasks.query.filter(
CaseTasks.id == task_id
).delete()
def delete_task_comment(task_id, comment_id):
comment = Comments.query.filter(
Comments.comment_id == comment_id,
Comments.comment_user_id == current_user.id
).first()
if not comment:
return False, "You are not allowed to delete this comment"
TaskComments.query.filter(
TaskComments.comment_task_id == task_id,
TaskComments.comment_id == comment_id
).delete()
db.session.delete(comment)
db.session.commit()
return True, "Comment deleted"
def get_tasks_cases_mapping(open_cases_only=False):
condition = Cases.close_date == None if open_cases_only else True
return CaseTasks.query.filter(
condition
).with_entities(
CaseTasks.task_case_id,
CaseTasks.task_status_id
).join(
CaseTasks.case
).all()