chg: [ail orgs] refactor organisations + add org level to investigations

This commit is contained in:
terrtia 2024-08-27 15:48:11 +02:00
parent 69471e0d37
commit ff59343bb7
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
12 changed files with 494 additions and 202 deletions

View file

@ -23,6 +23,7 @@ sys.path.append(os.environ['AIL_BIN'])
# Import Project packages # Import Project packages
################################## ##################################
from lib import ail_core from lib import ail_core
from lib import ail_orgs
from lib import ConfigLoader from lib import ConfigLoader
from lib import Tag from lib import Tag
from lib.exceptions import UpdateInvestigationError from lib.exceptions import UpdateInvestigationError
@ -47,9 +48,6 @@ def sanityze_uuid(UUID):
sanityzed_uuid = uuid.UUID(hex=UUID, version=4) sanityzed_uuid = uuid.UUID(hex=UUID, version=4)
return str(sanityzed_uuid).replace('-', '') return str(sanityzed_uuid).replace('-', '')
def exists_obj_type(obj_type):
return obj_type in ['domain', 'item', 'pgp', 'cryptocurrency', 'decoded', 'screenshot', 'username']
def generate_uuid(): def generate_uuid():
return str(uuid.uuid4()).replace('-', '') return str(uuid.uuid4()).replace('-', '')
@ -105,6 +103,42 @@ class Investigation(object):
def get_name(self): def get_name(self):
return r_tracking.hget(f'investigations:data:{self.uuid}', 'name') return r_tracking.hget(f'investigations:data:{self.uuid}', 'name')
## LEVEL ##
def get_level(self):
return int(r_tracking.hget(f'investigations:data:{self.uuid}', 'level'))
def set_level(self, level, org_uuid):
r_tracking.hset(f'investigations:data:{self.uuid}', 'level', level)
# Global
if level == 1:
r_tracking.sadd('investigations', self.uuid)
# Org
elif level == 2:
self.add_to_org(org_uuid)
def reset_level(self, old_level, new_level, new_org_uuid):
if old_level == 1:
r_tracking.srem('investigations', self.uuid)
# Org
elif old_level == 2:
ail_orgs.remove_obj_to_org(self.get_org(), 'investigation', self.uuid)
self.set_level(new_level, new_org_uuid)
## ORG ##
def get_creator_org(self):
return r_tracking.hget(f'investigations:data:{self.uuid}', 'creator_org')
def get_org(self):
return r_tracking.hget(f'investigations:data:{self.uuid}', 'org')
def add_to_org(self, org_uuid):
r_tracking.hset(f'investigations:data:{self.uuid}', 'org', org_uuid)
ail_orgs.add_obj_to_org(org_uuid, 'investigation', self.uuid)
## -ORG- ##
def get_threat_level(self): def get_threat_level(self):
try: try:
return int(r_tracking.hget(f'investigations:data:{self.uuid}', 'threat_level')) return int(r_tracking.hget(f'investigations:data:{self.uuid}', 'threat_level'))
@ -166,6 +200,8 @@ class Investigation(object):
'analysis': analysis, 'analysis': analysis,
'tags': list(self.get_tags()), 'tags': list(self.get_tags()),
'user_creator': self.get_creator_user(), 'user_creator': self.get_creator_user(),
'level': self.get_level(),
'org': self.get_org(),
'date': self.get_date(), 'date': self.get_date(),
'timestamp': self.get_timestamp(r_str=r_str), 'timestamp': self.get_timestamp(r_str=r_str),
'last_change': self.get_last_change(r_str=r_str), 'last_change': self.get_last_change(r_str=r_str),
@ -270,14 +306,22 @@ class Investigation(object):
self.unregister_object(obj_id, obj_type, subtype=subtype) self.unregister_object(obj_id, obj_type, subtype=subtype)
r_tracking.srem('investigations:all', self.uuid) r_tracking.srem('investigations:all', self.uuid)
# level
level = self.get_level()
if level == 1:
r_tracking.srem('investigations', self.uuid)
elif level == 2:
ail_orgs.remove_obj_to_org(self.get_org(), 'investigation', self.uuid)
# user map # user map
r_tracking.srem(f'investigations:user:{self.get_creator_user()}', self.uuid) r_tracking.srem(f'investigations:user:{self.get_creator_user()}', self.uuid)
# metadata # metadata
r_tracking.delete(f'investigations:data:{self.uuid}') r_tracking.delete(f'investigations:data:{self.uuid}')
r_tracking.delete(f'investigations:tags:{self.uuid}') r_tracking.delete(f'investigations:tags:{self.uuid}')
r_tracking.delete(f'investigations:misp:{self.uuid}') r_tracking.delete(f'investigations:misp:{self.uuid}')
return self.uuid
##-- Class --## ## -- -- ##
def get_all_investigations(): def get_all_investigations():
return r_tracking.smembers('investigations:all') return r_tracking.smembers('investigations:all')
@ -285,6 +329,12 @@ def get_all_investigations():
def exists_investigation(investigation_uuid): def exists_investigation(investigation_uuid):
return r_tracking.sismember('investigations:all', investigation_uuid) return r_tracking.sismember('investigations:all', investigation_uuid)
def get_global_investigations():
return r_tracking.smembers('investigations')
def get_org_investigations(org_uuid):
return ail_orgs.get_org_objs_by_type(org_uuid, 'investigation')
# created by user # created by user
def get_user_all_investigations(user_id): def get_user_all_investigations(user_id):
return r_tracking.smembers(f'investigations:user:{user_id}') return r_tracking.smembers(f'investigations:user:{user_id}')
@ -309,8 +359,8 @@ def _set_timestamp(investigation_uuid, timestamp):
# analysis - threat level - info - date - creator # analysis - threat level - info - date - creator
def _re_create_investagation(investigation_uuid, user_id, date, name, threat_level, analysis, info, tags, last_change, timestamp, misp_events): def _re_create_investigation(investigation_uuid, user_org, user_id, level, date, name, threat_level, analysis, info, tags, last_change, timestamp, misp_events):
create_investigation(user_id, date, name, threat_level, analysis, info, tags=tags, investigation_uuid=investigation_uuid) create_investigation(user_org, user_id, level, date, name, threat_level, analysis, info, tags=tags, investigation_uuid=investigation_uuid)
if timestamp: if timestamp:
_set_timestamp(investigation_uuid, timestamp) _set_timestamp(investigation_uuid, timestamp)
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
@ -323,7 +373,7 @@ def _re_create_investagation(investigation_uuid, user_id, date, name, threat_lev
# # TODO: limit description + name # # TODO: limit description + name
# # TODO: sanitize tags # # TODO: sanitize tags
# # TODO: sanitize date # # TODO: sanitize date
def create_investigation(user_id, date, name, threat_level, analysis, info, tags=[], investigation_uuid=None): def create_investigation(user_org, user_id, level, date, name, threat_level, analysis, info, tags=[], investigation_uuid=None):
if investigation_uuid: if investigation_uuid:
if not is_valid_uuid_v4(investigation_uuid): if not is_valid_uuid_v4(investigation_uuid):
investigation_uuid = generate_uuid() investigation_uuid = generate_uuid()
@ -331,12 +381,15 @@ def create_investigation(user_id, date, name, threat_level, analysis, info, tags
investigation_uuid = generate_uuid() investigation_uuid = generate_uuid()
r_tracking.sadd('investigations:all', investigation_uuid) r_tracking.sadd('investigations:all', investigation_uuid)
# user map # user map
r_tracking.sadd(f'investigations:user:{user_id}', investigation_uuid) r_tracking.sadd(f'investigations:user:{user_id}', investigation_uuid) # TODO REFACTOR ME
# metadata # metadata
r_tracking.hset(f'investigations:data:{investigation_uuid}', 'creator_org', user_org)
r_tracking.hset(f'investigations:data:{investigation_uuid}', 'creator_user', user_id) r_tracking.hset(f'investigations:data:{investigation_uuid}', 'creator_user', user_id)
# TODO: limit info + name # TODO: limit info + name
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
investigation.set_level(level, user_org)
investigation.set_info(info) investigation.set_info(info)
#investigation.set_name(name) ############################################## #investigation.set_name(name) ##############################################
investigation.set_date(date) investigation.set_date(date)
@ -360,23 +413,61 @@ def get_all_investigations_meta(r_str=False):
investigations_meta.append(investigation.get_metadata(r_str=r_str)) investigations_meta.append(investigation.get_metadata(r_str=r_str))
return investigations_meta return investigations_meta
def get_investigations_selector(): def get_global_investigations_meta(r_str=False):
investigations_meta = []
for investigation_uuid in get_global_investigations():
investigation = Investigation(investigation_uuid)
investigations_meta.append(investigation.get_metadata(r_str=r_str))
return investigations_meta
def get_org_investigations_meta(org_uuid, r_str=False):
investigations_meta = []
for investigation_uuid in get_org_investigations(org_uuid):
investigation = Investigation(investigation_uuid)
investigations_meta.append(investigation.get_metadata(r_str=r_str))
return investigations_meta
def get_investigations_selector(org_uuid):
l_investigations = [] l_investigations = []
for investigation_uuid in get_all_investigations(): for investigation_uuid in get_global_investigations():
investigation = Investigation(investigation_uuid)
name = investigation.get_info()
l_investigations.append({"id": investigation_uuid, "name": name})
for investigation_uuid in get_org_investigations(org_uuid):
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
name = investigation.get_info() name = investigation.get_info()
l_investigations.append({"id": investigation_uuid, "name": name}) l_investigations.append({"id": investigation_uuid, "name": name})
return l_investigations return l_investigations
#{id:'8dc4b81aeff94a9799bd70ba556fa345',name:"Paris"} #### ACL ####
def check_access_acl(inv, user_org, is_admin=False):
if is_admin:
return True
level = inv.get_level()
if level == 1:
return True
if level == 2:
return ail_orgs.check_access_acl(inv, user_org, is_admin=is_admin)
else:
return False
def api_check_access_acl(inv_uuid, user_org, is_admin=False):
if not check_access_acl(inv_uuid, user_org, is_admin=is_admin):
return {"status": "error", "reason": "Access Denied"}, 403
#### API #### #### API ####
def api_get_investigation(investigation_uuid): # TODO check if is UUIDv4 def api_get_investigation(user_org, is_admin, investigation_uuid): # TODO check if is UUIDv4
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
if not investigation.exists(): if not investigation.exists():
return {'status': 'error', 'reason': 'Investigation Not Found'}, 404 return {'status': 'error', 'reason': 'Investigation Not Found'}, 404
res = api_check_access_acl(investigation, user_org, is_admin=is_admin)
if res:
return res
meta = investigation.get_metadata(options={'objects'}, r_str=False) meta = investigation.get_metadata(options={'objects'}, r_str=False)
# objs = [] # objs = []
@ -392,6 +483,7 @@ def api_get_investigation(investigation_uuid): # TODO check if is UUIDv4
# # TODO: SANITYZE Fields # # TODO: SANITYZE Fields
# # TODO: Name ????? # # TODO: Name ?????
def api_add_investigation(json_dict): def api_add_investigation(json_dict):
user_org = json_dict.get('user_org')
user_id = json_dict.get('user_id') user_id = json_dict.get('user_id')
name = json_dict.get('name') ##### mandatory ? name = json_dict.get('name') ##### mandatory ?
name = escape(name) name = escape(name)
@ -401,6 +493,14 @@ def api_add_investigation(json_dict):
# # TODO: sanityze date # # TODO: sanityze date
date = json_dict.get('date') date = json_dict.get('date')
level = json_dict.get('level', 1)
try:
level = int(level)
except TypeError:
level = 1
if level not in range(1, 3):
level = 1
info = json_dict.get('info', '') info = json_dict.get('info', '')
info = escape(info) info = escape(info)
info = info[:1000] info = info[:1000]
@ -409,13 +509,13 @@ def api_add_investigation(json_dict):
return {"status": "error", "reason": "Invalid/Disabled tags"}, 400 return {"status": "error", "reason": "Invalid/Disabled tags"}, 400
try: try:
res = create_investigation(user_id, date, name, threat_level, analysis, info, tags=tags) res = create_investigation(user_org, user_id, level, date, name, threat_level, analysis, info, tags=tags)
except UpdateInvestigationError as e: except UpdateInvestigationError as e:
return e.message, 400 return e.message, 400
return res, 200 return res, 200
# # TODO: edit threat level / status # # TODO: edit threat level / status
def api_edit_investigation(json_dict): def api_edit_investigation(user_org, user_id, is_admin, json_dict):
investigation_uuid = json_dict.get('uuid', '').replace(' ', '') investigation_uuid = json_dict.get('uuid', '').replace(' ', '')
if not is_valid_uuid_v4(investigation_uuid): if not is_valid_uuid_v4(investigation_uuid):
return {"status": "error", "reason": "Invalid Investigation uuid"}, 400 return {"status": "error", "reason": "Invalid Investigation uuid"}, 400
@ -423,6 +523,9 @@ def api_edit_investigation(json_dict):
if not exists_investigation(investigation_uuid): if not exists_investigation(investigation_uuid):
return {"status": "error", "reason": "Investigation not found"}, 404 return {"status": "error", "reason": "Investigation not found"}, 404
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
res = api_check_access_acl(investigation, user_org, is_admin=is_admin)
if res:
return res
name = json_dict.get('name') ##### mandatory ? name = json_dict.get('name') ##### mandatory ?
name = escape(name) name = escape(name)
@ -445,6 +548,17 @@ def api_edit_investigation(json_dict):
if not Tag.are_enabled_tags(tags): if not Tag.are_enabled_tags(tags):
return {"status": "error", "reason": "Invalid/Disabled tags"}, 400 return {"status": "error", "reason": "Invalid/Disabled tags"}, 400
level = json_dict.get('level', 1)
try:
level = int(level)
except TypeError:
level = 1
if level not in range(1, 3):
level = 1
old_level = investigation.get_level()
if level != old_level:
investigation.reset_level(old_level, level, user_org)
investigation.set_info(info) investigation.set_info(info)
investigation.set_name(name) investigation.set_name(name)
investigation.set_tags(tags) investigation.set_tags(tags)
@ -454,7 +568,7 @@ def api_edit_investigation(json_dict):
return investigation_uuid, 200 return investigation_uuid, 200
def api_delete_investigation(json_dict): def api_delete_investigation(user_org, user_id, is_admin, json_dict):
investigation_uuid = json_dict.get('uuid', '').replace(' ', '') investigation_uuid = json_dict.get('uuid', '').replace(' ', '')
if not is_valid_uuid_v4(investigation_uuid): if not is_valid_uuid_v4(investigation_uuid):
return {"status": "error", "reason": "Invalid Investigation uuid"}, 400 return {"status": "error", "reason": "Invalid Investigation uuid"}, 400
@ -462,10 +576,13 @@ def api_delete_investigation(json_dict):
if not exists_investigation(investigation_uuid): if not exists_investigation(investigation_uuid):
return {"status": "error", "reason": "Investigation not found"}, 404 return {"status": "error", "reason": "Investigation not found"}, 404
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
res = api_check_access_acl(investigation, user_org, is_admin=is_admin)
if res:
return res
res = investigation.delete() res = investigation.delete()
return res, 200 return res, 200
def api_register_object(json_dict): def api_register_object(user_org, user_id, is_admin, json_dict):
investigation_uuid = json_dict.get('uuid', '').replace(' ', '') investigation_uuid = json_dict.get('uuid', '').replace(' ', '')
if not is_valid_uuid_v4(investigation_uuid): if not is_valid_uuid_v4(investigation_uuid):
return {"status": "error", "reason": f"Invalid Investigation uuid: {investigation_uuid}"}, 400 return {"status": "error", "reason": f"Invalid Investigation uuid: {investigation_uuid}"}, 400
@ -473,6 +590,9 @@ def api_register_object(json_dict):
if not exists_investigation(investigation_uuid): if not exists_investigation(investigation_uuid):
return {"status": "error", "reason": f"Investigation not found: {investigation_uuid}"}, 404 return {"status": "error", "reason": f"Investigation not found: {investigation_uuid}"}, 404
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
res = api_check_access_acl(investigation, user_org, is_admin=is_admin)
if res:
return res
obj_type = json_dict.get('type', '').replace(' ', '') obj_type = json_dict.get('type', '').replace(' ', '')
if obj_type not in ail_core.get_all_objects(): if obj_type not in ail_core.get_all_objects():
@ -489,7 +609,7 @@ def api_register_object(json_dict):
res = investigation.register_object(obj_id, obj_type, subtype, comment=comment) res = investigation.register_object(obj_id, obj_type, subtype, comment=comment)
return res, 200 return res, 200
def api_unregister_object(json_dict): def api_unregister_object(user_org, user_id, is_admin, json_dict):
investigation_uuid = json_dict.get('uuid', '').replace(' ', '') investigation_uuid = json_dict.get('uuid', '').replace(' ', '')
if not is_valid_uuid_v4(investigation_uuid): if not is_valid_uuid_v4(investigation_uuid):
return {"status": "error", "reason": f"Invalid Investigation uuid: {investigation_uuid}"}, 400 return {"status": "error", "reason": f"Invalid Investigation uuid: {investigation_uuid}"}, 400
@ -497,6 +617,9 @@ def api_unregister_object(json_dict):
if not exists_investigation(investigation_uuid): if not exists_investigation(investigation_uuid):
return {"status": "error", "reason": f"Investigation not found: {investigation_uuid}"}, 404 return {"status": "error", "reason": f"Investigation not found: {investigation_uuid}"}, 404
investigation = Investigation(investigation_uuid) investigation = Investigation(investigation_uuid)
res = api_check_access_acl(investigation, user_org, is_admin=is_admin)
if res:
return res
obj_type = json_dict.get('type', '').replace(' ', '') obj_type = json_dict.get('type', '').replace(' ', '')
subtype = json_dict.get('subtype', '') subtype = json_dict.get('subtype', '')

View file

@ -26,6 +26,10 @@ def is_valid_token(token):
def get_user_from_token(token): def get_user_from_token(token):
return ail_users.get_token_user(token) return ail_users.get_token_user(token)
def get_basic_user_meta(token):
user_id = get_user_from_token(token)
return ail_users.get_user_org(user_id), user_id, ail_users.is_in_role(user_id, 'admin')
def is_user_in_role(role, token): # verify_user_role def is_user_in_role(role, token): # verify_user_role
# User without API # User without API
if role == 'user_no_api': if role == 'user_no_api':

222
bin/lib/ail_orgs.py Executable file
View file

@ -0,0 +1,222 @@
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
import os
import sys
from datetime import datetime
sys.path.append(os.environ['AIL_BIN'])
##################################
# Import Project packages
##################################
from lib import ail_logger
from lib.ail_core import is_valid_uuid_v4, generate_uuid
from lib.ConfigLoader import ConfigLoader
# LOGS
access_logger = ail_logger.get_access_config()
# Config
config_loader = ConfigLoader()
r_serv_db = config_loader.get_db_conn("Kvrocks_DB")
r_data = config_loader.get_db_conn("Kvrocks_DB") # TODO MOVE DEFAULT DB
# r_cache = config_loader.get_redis_conn("Redis_Cache")
config_loader = None
#### ORGANISATIONS ####
# TODO EDIT
# TODO DELETE CHECK
# TODO DELETE OBJS
# TODO ORG View
# TODO TAGS
# TODO TAGS USERS ????
# TODO Check if ORG name is UNIQUE
def get_orgs():
return r_serv_db.smembers(f'ail:orgs')
def is_user_in_org(org_uuid, user_id):
return r_serv_db.sadd(f'ail:org:{org_uuid}:users', user_id)
#### ORGANISATION ####
class Organisation:
def __init__(self, org_uuid):
self.uuid = org_uuid
def exists(self):
return r_serv_db.exists(f'ail:org:{self.uuid}')
def _get_field(self, field):
return r_serv_db.hget(f'ail:org:{self.uuid}', field)
def _set_fields(self, field, value):
return r_serv_db.hset(f'ail:org:{self.uuid}', field, value)
def get_uuid(self):
return self.uuid
def get_date_created(self):
return self._get_field('date_created')
def get_date_modified(self):
return self._get_field('date_modified')
def get_description(self):
return self._get_field('description')
def get_name(self):
return self._get_field('name')
def get_nationality(self):
return self._get_field('nationality')
def get_creator(self):
return self._get_field('creator')
def get_org_type(self):
return self._get_field('type')
def get_sector(self):
return self._get_field('sector')
def get_tags(self): # TODO
pass
def get_logo(self):
pass
def get_users(self):
return r_serv_db.smembers(f'ail:org:{self.uuid}:users')
def get_nb_users(self):
return r_serv_db.scard(f'ail:org:{self.uuid}:users')
def get_meta(self, options=set()):
meta = {'uuid': self.uuid}
if 'name' in options:
meta['name'] = self._get_field('name')
if 'description' in options:
meta['description'] = self._get_field('description')
if 'creator' in options:
meta['creator'] = self._get_field('creator')
if 'date_created' in options:
meta['date_created'] = self._get_field('date_created')
return meta
def add_user(self, user_id):
r_serv_db.sadd(f'ail:org:{self.uuid}:users', user_id)
r_serv_db.hset(f'ail:user:metadata:{user_id}', 'org', self.uuid)
def remove_user(self, user_id):
r_serv_db.srem(f'ail:org:{self.uuid}:users', user_id)
r_serv_db.hdel(f'ail:user:metadata:{user_id}', 'org')
def remove_users(self):
for user_id in self.get_users():
self.remove_user(user_id)
def create(self, creator, name, description=None, nationality=None, sector=None, org_type=None, logo=None):
r_serv_db.sadd(f'ail:orgs', self.uuid)
self._set_fields('creator', creator)
self._set_fields('name', name)
self._set_fields('description', description)
if nationality:
self._set_fields('nationality', nationality)
if sector:
self._set_fields('sector', sector)
if org_type:
self._set_fields('type', org_type)
# if logo:
current = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
self._set_fields('date_created', current)
self._set_fields('date_modified', current)
def edit(self):
pass
def delete(self): # TODO CHANGE ACL ASSOCIATED WITH ORGS -> Tracker, Investigation, objects, ...
self.remove_users()
r_serv_db.delete(f'ail:org:{self.uuid}')
r_serv_db.srem(f'ail:orgs', self.uuid)
def exists_org(org_uuid):
return r_serv_db.exists(f'ail:org:{org_uuid}')
def create_org(name, description, uuid=None, nationality=None, sector=None, org_type=None, logo=None): # contacts ?????
if uuid is None:
uuid = generate_uuid()
else:
if exists_org(uuid):
raise Exception('Organisation already exists') # TODO CUSTOM ERROR
org = Organisation(uuid)
org.create(name, description, nationality=nationality, sector=sector, org_type=org_type, logo=logo)
def get_org_objs_by_type(org_uuid, obj_type):
return r_serv_db.smembers(f'org:{org_uuid}:{obj_type}')
def add_obj_to_org(org_uuid, obj_type, obj_gid): # ADD set UUID -> object types ???
r_serv_db.sadd(f'org:{org_uuid}:{obj_type}', obj_gid)
def remove_obj_to_org(org_uuid, obj_type, obj_gid):
r_serv_db.srem(f'org:{org_uuid}:{obj_type}', obj_gid)
## --ORGANISATION-- ##
def check_access_acl(obj, user_org, is_admin=False):
if is_admin:
return True
return obj.get_org() == user_org
#### API ####
def api_get_orgs_meta():
meta = {'orgs': []}
options = {'date_created', 'description', 'name'}
for org_uuid in get_orgs():
org = Organisation(org_uuid)
meta['orgs'].append(org.get_meta(options=options))
return meta
def api_create_org(creator, org_uuid, name, ip_address, description=None):
if not is_valid_uuid_v4(org_uuid):
return {'status': 'error', 'reason': 'Invalid UUID'}, 400
if exists_org(org_uuid):
return {'status': 'error', 'reason': 'Org already exists'}, 400
org = Organisation(org_uuid)
org.create(creator, name, description=description)
access_logger.info(f'Created org {org_uuid}', extra={'user_id': creator, 'ip_address': ip_address})
return org.get_uuid(), 200
def api_delete_org(org_uuid, admin_id, ip_address): # TODO check if nothing is linked to this org
if not exists_org(org_uuid):
return {'status': 'error', 'reason': 'Org not found'}, 404
access_logger.warning(f'Deleted org {org_uuid}', extra={'user_id': admin_id, 'ip_address': ip_address})
org = Organisation(org_uuid)
org.delete()
return org_uuid, 200
# if __name__ == '__main__':
# user_id = 'admin@admin.test'
# instance_name = 'AIL TEST'
# delete_user_otp(user_id)
# # q = get_user_otp_qr_code(user_id, instance_name)
# # print(q)

View file

@ -20,7 +20,7 @@ sys.path.append(os.environ['AIL_BIN'])
# Import Project packages # Import Project packages
################################## ##################################
from lib import ail_logger from lib import ail_logger
from lib.ail_core import is_valid_uuid_v4 from lib import ail_orgs
from lib.ConfigLoader import ConfigLoader from lib.ConfigLoader import ConfigLoader
@ -42,145 +42,6 @@ config_loader = None
regex_password = r'^(?=(.*\d){2})(?=.*[a-z])(?=.*[A-Z]).{10,100}$' regex_password = r'^(?=(.*\d){2})(?=.*[a-z])(?=.*[A-Z]).{10,100}$'
regex_password = re.compile(regex_password) regex_password = re.compile(regex_password)
#### ORGANISATIONS ####
# TODO EDIT
# TODO DELETE CHECK
# TODO ORG View
# TODO TAGS
# TODO TAGS USERS ????
# TODO Check if ORG name is UNIQUE
def get_orgs():
return r_serv_db.smembers(f'ail:orgs')
def is_user_in_org(org_uuid, user_id):
return r_serv_db.sadd(f'ail:org:{org_uuid}:users', user_id)
class Organisation:
def __init__(self, org_uuid):
self.uuid = org_uuid
def exists(self):
return r_serv_db.exists(f'ail:org:{self.uuid}')
def _get_field(self, field):
return r_serv_db.hget(f'ail:org:{self.uuid}', field)
def _set_fields(self, field, value):
return r_serv_db.hset(f'ail:org:{self.uuid}', field, value)
def get_uuid(self):
return self.uuid
def get_date_created(self):
date = self._get_field('date_created')
def get_date_modified(self):
date = self._get_field('date_modified')
def get_description(self):
return self._get_field('description')
def get_name(self):
return self._get_field('name')
def get_nationality(self):
return self._get_field('nationality')
def get_creator(self):
return self._get_field('creator')
def get_org_type(self):
return self._get_field('type')
def get_sector(self):
return self._get_field('sector')
def get_tags(self): # TODO
pass
def get_logo(self):
pass
def get_users(self):
return r_serv_db.smembers(f'ail:org:{self.uuid}:users')
def get_nb_users(self):
return r_serv_db.scard(f'ail:org:{self.uuid}:users')
def get_meta(self, options=set()):
meta = {'uuid': self.uuid}
if 'name' in options:
meta['name'] = self._get_field('name')
if 'description' in options:
meta['description'] = self._get_field('description')
if 'creator' in options:
meta['creator'] = self._get_field('creator')
if 'date_created' in options:
meta['date_created'] = self._get_field('date_created')
return meta
def add_user(self, user_id):
if exists_user(user_id) and not get_user_org(user_id):
r_serv_db.sadd(f'ail:org:{self.uuid}:users', user_id)
r_serv_db.hset(f'ail:user:metadata:{user_id}', 'org', self.uuid)
def remove_user(self, user_id):
r_serv_db.srem(f'ail:org:{self.uuid}:users', user_id)
r_serv_db.hdel(f'ail:user:metadata:{user_id}', 'org')
def remove_users(self):
for user_id in self.get_users():
self.remove_user(user_id)
def create(self, creator, name, description=None, nationality=None, sector=None, org_type=None, logo=None):
r_serv_db.sadd(f'ail:orgs', self.uuid)
self._set_fields('creator', creator)
self._set_fields('name', name)
self._set_fields('description', description)
if nationality:
self._set_fields('nationality', nationality)
if sector:
self._set_fields('sector', sector)
if org_type:
self._set_fields('type', org_type)
#if logo:
current = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
self._set_fields('date_created', current)
self._set_fields('date_modified', current)
def edit(self):
pass
def delete(self): # TODO CHANGE ACL ASSOCIATED WITH ORGS -> Tracker, Investigation, objects, ...
self.remove_users()
r_serv_db.delete(f'ail:org:{self.uuid}')
r_serv_db.srem(f'ail:orgs', self.uuid)
def exists_org(org_uuid):
return r_serv_db.exists(f'ail:org:{org_uuid}')
def create_org(name, description, uuid=None, nationality=None, sector=None, org_type=None, logo=None): # contacts ?????
if uuid is None:
uuid = str(uuid4()) # TODO check if is uuidv4
else:
if exists_org(uuid):
raise Exception('Organisation already exists') # TODO CUSTOM ERROR
org = Organisation(uuid)
org.create( name, description, nationality=nationality, sector=sector, org_type=org_type, logo=logo)
## --ORGANISATIONS-- ##
#### SESSIONS #### #### SESSIONS ####
def get_sessions(): def get_sessions():
@ -680,6 +541,9 @@ class AILUser(UserMixin):
else: else:
return False return False
def is_admin(self):
return self.is_in_role('admin')
def get_role(self): def get_role(self):
return r_serv_db.hget(f'ail:user:metadata:{self.user_id}', 'role') return r_serv_db.hget(f'ail:user:metadata:{self.user_id}', 'role')
@ -698,33 +562,6 @@ class AILUser(UserMixin):
#### API #### #### API ####
def api_get_orgs_meta():
meta = {'orgs': []}
options = {'date_created', 'description', 'name'}
for org_uuid in get_orgs():
org = Organisation(org_uuid)
meta['orgs'].append(org.get_meta(options=options))
return meta
def api_create_org(creator, org_uuid, name, ip_address, description=None):
if not is_valid_uuid_v4(org_uuid):
return {'status': 'error', 'reason': 'Invalid UUID'}, 400
if exists_org(org_uuid):
return {'status': 'error', 'reason': 'Org already exists'}, 400
org = Organisation(org_uuid)
org.create(creator, name, description=description)
access_logger.info(f'Created org {org_uuid}', extra={'user_id': creator, 'ip_address': ip_address})
return org.get_uuid(), 200
def api_delete_org(org_uuid, admin_id, ip_address): # TODO check if nothing is linked to this org
if not exists_org(org_uuid):
return {'status': 'error', 'reason': 'Org not found'}, 404
access_logger.warning(f'Deleted org {org_uuid}', extra={'user_id': admin_id, 'ip_address': ip_address})
org = Organisation(org_uuid)
org.delete()
return org_uuid, 200
def api_get_users_meta(): def api_get_users_meta():
meta = {'users': []} meta = {'users': []}
options = {'api_key', 'creator', 'created_at', 'is_logged', 'last_edit', 'last_login', 'last_seen', 'last_seen_api', 'role', '2fa', 'otp_setup'} options = {'api_key', 'creator', 'created_at', 'is_logged', 'last_edit', 'last_login', 'last_seen', 'last_seen_api', 'role', '2fa', 'otp_setup'}

View file

@ -9,6 +9,8 @@ import sys
import importlib.util import importlib.util
from lib.ail_core import get_ail_uuid
sys.path.append(os.environ['AIL_BIN']) sys.path.append(os.environ['AIL_BIN'])
################################## ##################################
# Import Project packages # Import Project packages
@ -260,7 +262,7 @@ def investigations_migration():
for investigation_uuid in old_Investigations.get_all_investigations(): for investigation_uuid in old_Investigations.get_all_investigations():
old_investigation = old_Investigations.Investigation(investigation_uuid) old_investigation = old_Investigations.Investigation(investigation_uuid)
meta = old_investigation.get_metadata() meta = old_investigation.get_metadata()
Investigations._re_create_investagation(meta['uuid'], meta['user_creator'], meta['date'], meta['name'], meta['threat_level'], meta['analysis'], meta['info'], meta['tags'], meta['last_change'], meta['timestamp'], meta['misp_events']) Investigations._re_create_investigation(meta['uuid'], get_ail_uuid(), meta['user_creator'], 1, meta['date'], meta['name'], meta['threat_level'], meta['analysis'], meta['info'], meta['tags'], meta['last_change'], meta['timestamp'], meta['misp_events'])
new_investigation = Investigations.Investigation(meta['uuid']) new_investigation = Investigations.Investigation(meta['uuid'])
for dict_obj in old_investigation.get_objects(): for dict_obj in old_investigation.get_objects():
new_investigation.register_object(dict_obj['id'], dict_obj['type'], dict_obj['subtype']) new_investigation.register_object(dict_obj['id'], dict_obj['type'], dict_obj['subtype'])

View file

@ -12,6 +12,7 @@ sys.path.append(os.environ['AIL_HOME'])
################################## ##################################
from update.bin.ail_updater import AIL_Updater from update.bin.ail_updater import AIL_Updater
from lib import ail_users from lib import ail_users
from lib import Investigations
from lib.ConfigLoader import ConfigLoader from lib.ConfigLoader import ConfigLoader
from lib import chats_viewer from lib import chats_viewer
@ -28,11 +29,22 @@ if __name__ == '__main__':
config_loader = None config_loader = None
date = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') date = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
for user_id in ail_users.get_users(): # ORGS
# TODO CREATE DEFAULT ORG
# USERS
print('Updating Users ...')
for user_id in ail_users.get_users(): # TODO ORG
r_serv_db.hset(f'ail:user:metadata:{user_id}', 'creator', 'admin@admin.test') r_serv_db.hset(f'ail:user:metadata:{user_id}', 'creator', 'admin@admin.test')
r_serv_db.hset(f'ail:user:metadata:{user_id}', 'created_at', date) r_serv_db.hset(f'ail:user:metadata:{user_id}', 'created_at', date)
r_serv_db.hset(f'ail:user:metadata:{user_id}', 'last_edit', date) r_serv_db.hset(f'ail:user:metadata:{user_id}', 'last_edit', date)
# INVESTIGATIONS
print('Updating Investigations ...')
for inv_uuid in Investigations.get_all_investigations(): # TODO Creator ORG
inv = Investigations.Investigation(inv_uuid)
inv.set_level(1, None)
chats_viewer.fix_chats_with_messages() chats_viewer.fix_chats_with_messages()
updater = Updater('v5.7') updater = Updater('v5.7')

View file

@ -19,6 +19,7 @@ sys.path.append(os.environ['AIL_BIN'])
from lib import ail_api from lib import ail_api
from lib import ail_core from lib import ail_core
from lib import ail_updates from lib import ail_updates
from lib import ail_logger
from lib import crawlers from lib import crawlers
from lib import chats_viewer from lib import chats_viewer
@ -32,6 +33,10 @@ from lib.objects import Titles
from importer.FeederImporter import api_add_json_feeder_to_queue from importer.FeederImporter import api_add_json_feeder_to_queue
# LOGS
# access_logger = ail_logger.get_access_config()
# ============ BLUEPRINT ============ # ============ BLUEPRINT ============
api_rest = Blueprint('api_rest', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates')) api_rest = Blueprint('api_rest', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates'))
@ -66,6 +71,7 @@ def token_required(user_role):
return create_json_response({'status': 'error', 'reason': 'Access Forbidden'}, 403) return create_json_response({'status': 'error', 'reason': 'Access Forbidden'}, 403)
else: else:
# User Authenticated + In Role # User Authenticated + In Role
# print(funct.__name__)
return funct(*args, **kwargs) return funct(*args, **kwargs)
else: else:
return create_json_response({'status': 'error', 'reason': 'Internal'}, 400) return create_json_response({'status': 'error', 'reason': 'Internal'}, 400)
@ -245,7 +251,9 @@ def objects_titles_download_unsafe():
@api_rest.route("api/v1/investigation/<investigation_uuid>", methods=['GET']) # TODO options @api_rest.route("api/v1/investigation/<investigation_uuid>", methods=['GET']) # TODO options
@token_required('read_only') @token_required('read_only')
def v1_investigation(investigation_uuid): def v1_investigation(investigation_uuid):
r = Investigations.api_get_investigation(investigation_uuid) user_token = get_auth_from_header()
user_org, user_id, is_admin = ail_api.get_basic_user_meta(user_token)
r = Investigations.api_get_investigation(user_org, is_admin, investigation_uuid)
return create_json_response(r[0], r[1]) return create_json_response(r[0], r[1])
# TODO CATCH REDIRECT # TODO CATCH REDIRECT

View file

@ -42,17 +42,27 @@ def create_json_response(data, status_code):
@login_required @login_required
@login_read_only @login_read_only
def investigations_dashboard(): def investigations_dashboard():
investigations = Investigations.get_all_investigations_meta(r_str=True) inv_global = Investigations.get_global_investigations_meta(r_str=True)
inv_org = Investigations.get_org_investigations_meta(current_user.get_org(), r_str=True)
return render_template("investigations.html", bootstrap_label=bootstrap_label, return render_template("investigations.html", bootstrap_label=bootstrap_label,
investigations=investigations) inv_global=inv_global, inv_org=inv_org)
@investigations_b.route("/investigation", methods=['GET']) ## FIXME: add /view ???? @investigations_b.route("/investigation", methods=['GET']) ## FIXME: add /view ????
@login_required @login_required
@login_read_only @login_read_only
def show_investigation(): def show_investigation():
user_org = current_user.get_org()
# user_id = current_user.get_user_id()
is_admin = current_user.is_admin()
investigation_uuid = request.args.get("uuid") investigation_uuid = request.args.get("uuid")
investigation = Investigations.Investigation(investigation_uuid) investigation = Investigations.Investigation(investigation_uuid)
if not investigation.exists():
create_json_response({'status': 'error', 'reason': 'Investigation Not Found'}, 404)
res = Investigations.api_check_access_acl(investigation, user_org, is_admin=is_admin)
if res:
return create_json_response(res[0], res[1])
metadata = investigation.get_metadata(r_str=True) metadata = investigation.get_metadata(r_str=True)
objs = [] objs = []
for obj in investigation.get_objects(): for obj in investigation.get_objects():
@ -71,6 +81,8 @@ def show_investigation():
def add_investigation(): def add_investigation():
if request.method == 'POST': if request.method == 'POST':
user_id = current_user.get_user_id() user_id = current_user.get_user_id()
user_org = current_user.get_org()
level = request.form.get("investigation_level")
name = request.form.get("investigation_name") name = request.form.get("investigation_name")
date = request.form.get("investigation_date") date = request.form.get("investigation_date")
threat_level = request.form.get("threat_level") threat_level = request.form.get("threat_level")
@ -93,7 +105,7 @@ def add_investigation():
galaxies_tags = [] galaxies_tags = []
tags = taxonomies_tags + galaxies_tags tags = taxonomies_tags + galaxies_tags
input_dict = {"user_id": user_id, "name": name, input_dict = {"user_org": user_org, "user_id": user_id, "level": level, "name": name,
"threat_level": threat_level, "date": date, "threat_level": threat_level, "date": date,
"analysis": analysis, "info": info, "tags": tags} "analysis": analysis, "info": info, "tags": tags}
res = Investigations.api_add_investigation(input_dict) res = Investigations.api_add_investigation(input_dict)
@ -108,10 +120,13 @@ def add_investigation():
@investigations_b.route("/investigation/edit", methods=['GET', 'POST']) @investigations_b.route("/investigation/edit", methods=['GET', 'POST'])
@login_required @login_required
@login_analyst @login_analyst
def edit_investigation(): def edit_investigation(): # TODO CHECK ACL
if request.method == 'POST': if request.method == 'POST':
user_org = current_user.get_org()
user_id = current_user.get_user_id() user_id = current_user.get_user_id()
is_admin = current_user.is_admin()
investigation_uuid = request.form.get("investigation_uuid") investigation_uuid = request.form.get("investigation_uuid")
level = request.form.get("investigation_level")
name = request.form.get("investigation_name") name = request.form.get("investigation_name")
date = request.form.get("investigation_date") date = request.form.get("investigation_date")
threat_level = request.form.get("threat_level") threat_level = request.form.get("threat_level")
@ -135,10 +150,10 @@ def edit_investigation():
galaxies_tags = [] galaxies_tags = []
tags = taxonomies_tags + galaxies_tags tags = taxonomies_tags + galaxies_tags
input_dict = {"user_id": user_id, "uuid": investigation_uuid, input_dict = {"user_id": user_id, "uuid": investigation_uuid, "level": level,
"name": name, "threat_level": threat_level, "name": name, "threat_level": threat_level,
"analysis": analysis, "info": info, "tags": tags} "analysis": analysis, "info": info, "tags": tags}
res = Investigations.api_edit_investigation(input_dict) res = Investigations.api_edit_investigation(user_org, user_id, is_admin, input_dict)
if res[1] != 200: if res[1] != 200:
return create_json_response(res[0], res[1]) return create_json_response(res[0], res[1])
@ -158,9 +173,12 @@ def edit_investigation():
@login_required @login_required
@login_analyst @login_analyst
def delete_investigation(): def delete_investigation():
user_org = current_user.get_org()
user_id = current_user.get_user_id()
is_admin = current_user.is_admin()
investigation_uuid = request.args.get('uuid') investigation_uuid = request.args.get('uuid')
input_dict = {"uuid": investigation_uuid} input_dict = {"uuid": investigation_uuid}
res = Investigations.api_delete_investigation(input_dict) res = Investigations.api_delete_investigation(user_org, user_id, is_admin, input_dict)
if res[1] != 200: if res[1] != 200:
return create_json_response(res[0], res[1]) return create_json_response(res[0], res[1])
return redirect(url_for('investigations_b.investigations_dashboard')) return redirect(url_for('investigations_b.investigations_dashboard'))
@ -169,6 +187,9 @@ def delete_investigation():
@login_required @login_required
@login_read_only @login_read_only
def register_investigation(): def register_investigation():
user_id = current_user.get_user_id()
user_org = current_user.get_org()
is_admin = current_user.is_admin()
investigations_uuid = request.args.get('uuids') investigations_uuid = request.args.get('uuids')
investigations_uuid = investigations_uuid.split(',') investigations_uuid = investigations_uuid.split(',')
@ -182,7 +203,7 @@ def register_investigation():
"type": object_type, "subtype": object_subtype} "type": object_type, "subtype": object_subtype}
if comment: if comment:
input_dict["comment"] = comment input_dict["comment"] = comment
res = Investigations.api_register_object(input_dict) res = Investigations.api_register_object(user_org, user_id, is_admin, input_dict)
if res[1] != 200: if res[1] != 200:
return create_json_response(res[0], res[1]) return create_json_response(res[0], res[1])
return redirect(url_for('investigations_b.investigations_dashboard', uuid=investigation_uuid)) return redirect(url_for('investigations_b.investigations_dashboard', uuid=investigation_uuid))
@ -191,13 +212,16 @@ def register_investigation():
@login_required @login_required
@login_read_only @login_read_only
def unregister_investigation(): def unregister_investigation():
user_id = current_user.get_user_id()
user_org = current_user.get_org()
is_admin = current_user.is_admin()
investigation_uuid = request.args.get('uuid') investigation_uuid = request.args.get('uuid')
object_type = request.args.get('type') object_type = request.args.get('type')
object_subtype = request.args.get('subtype') object_subtype = request.args.get('subtype')
object_id = request.args.get('id') object_id = request.args.get('id')
input_dict = {"uuid": investigation_uuid, "id": object_id, input_dict = {"uuid": investigation_uuid, "id": object_id,
"type": object_type, "subtype": object_subtype} "type": object_type, "subtype": object_subtype}
res = Investigations.api_unregister_object(input_dict) res = Investigations.api_unregister_object(user_org, user_id, is_admin, input_dict)
if res[1] != 200: if res[1] != 200:
return create_json_response(res[0], res[1]) return create_json_response(res[0], res[1])
return redirect(url_for('investigations_b.show_investigation', uuid=investigation_uuid)) return redirect(url_for('investigations_b.show_investigation', uuid=investigation_uuid))
@ -207,7 +231,7 @@ def unregister_investigation():
@login_required @login_required
@login_read_only @login_read_only
def get_investigations_selector_json(): def get_investigations_selector_json():
return jsonify(Investigations.get_investigations_selector()) return jsonify(Investigations.get_investigations_selector(current_user.get_org()))
@investigations_b.route("/object/gid") @investigations_b.route("/object/gid")
@login_required @login_required

View file

@ -20,6 +20,7 @@ sys.path.append(os.environ['AIL_BIN'])
# Import Project packages # Import Project packages
################################## ##################################
from lib import ail_updates from lib import ail_updates
from lib import ail_orgs
from lib import ail_users from lib import ail_users
from lib import d4 from lib import d4
from packages import git_status from packages import git_status
@ -307,7 +308,7 @@ def users_list():
@login_required @login_required
@login_admin @login_admin
def organisations_list(): def organisations_list():
meta = ail_users.api_get_orgs_meta() meta = ail_orgs.api_get_orgs_meta()
return render_template("orgs_list.html", meta=meta, acl_admin=True) return render_template("orgs_list.html", meta=meta, acl_admin=True)
@settings_b.route("/settings/create_organisation", methods=['GET']) @settings_b.route("/settings/create_organisation", methods=['GET'])
@ -328,7 +329,7 @@ def create_org_post():
name = request.form.get('name') name = request.form.get('name')
description = request.form.get('description') description = request.form.get('description')
r = ail_users.api_create_org(admin_id, org_uuid, name, request.remote_addr, description=description) r = ail_orgs.api_create_org(admin_id, org_uuid, name, request.remote_addr, description=description)
if r[1] != 200: if r[1] != 200:
return create_json_response(r[0], r[1]) return create_json_response(r[0], r[1])
else: else:
@ -344,7 +345,7 @@ def create_org_post():
def delete_org(): def delete_org():
admin_id = current_user.get_user_id() admin_id = current_user.get_user_id()
org_uuid = request.args.get('uuid') org_uuid = request.args.get('uuid')
r = ail_users.api_delete_org(org_uuid, admin_id, request.remote_addr) r = ail_orgs.api_delete_org(org_uuid, admin_id, request.remote_addr)
if r[1] != 200: if r[1] != 200:
return create_json_response(r[0], r[1]) return create_json_response(r[0], r[1])
else: else:

View file

@ -144,6 +144,19 @@
{% else %} {% else %}
Create a new Investigation Create a new Investigation
{% endif %} {% endif %}
<br>
<label class="mt-3" for="level_selector">View Level</label>
<select class="custom-select" id="level_selector" name="investigation_level">
{% if edit %}
<option value="1" {% if metadata['level'] == 1 %}selected{% endif %}><i class="fas fa-users"></i> Global</option>
<option value="2" {% if metadata['level'] == 2 %}selected{% endif %}><i class="fas fa-landmark"></i> My Organisation</option>
{% else %}
<option value="1" selected><i class="fas fa-users"></i> Global</option>
<option value="2"><i class="fas fa-landmark"></i> My Organisation</option>
{% endif %}
</select>
</div> </div>
</div> </div>

View file

@ -31,7 +31,7 @@
<div class="col-12 col-lg-10" id="core_content"> <div class="col-12 col-lg-10" id="core_content">
<h3 class="mt-2 text-secondary"> <h3 class="mt-2 text-secondary">
<i class="fas fa-microscope"></i> Investigations: <i class="fas fa-microscope"></i> Organisation Investigations:
</h3> </h3>
<table id="table_investigation" class="table table-striped border-primary"> <table id="table_investigation" class="table table-striped border-primary">
@ -45,7 +45,43 @@
</tr> </tr>
</thead> </thead>
<tbody style="font-size: 15px;"> <tbody style="font-size: 15px;">
{% for dict_investigation in investigations %} {% for dict_investigation in inv_org %}
<tr class="border-color: blue;">
<td>
<a href="{{ url_for('investigations_b.show_investigation') }}?uuid={{ dict_investigation['uuid'] }}">
{{ dict_investigation['info']}}
<div>
{% for tag in dict_investigation['tags'] %}
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
{% endfor %}
</div>
</a>
</td>
<td>{{ dict_investigation['date']}}</td>
<td>{{ dict_investigation['last_change']}}</td>
<td>{{ dict_investigation['info']}}</td>
<td>{{ dict_investigation['nb_objects']}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h3 class="mt-2 text-secondary">
<i class="fas fa-microscope"></i> Global Investigations:
</h3>
<table id="table_investigation" class="table table-striped border-primary">
<thead class="bg-dark text-white">
<tr>
<th>Name</th>
<th>Date</th>
<th>last modified</th>
<td>Info</td>
<th>Nb Objects</th>
</tr>
</thead>
<tbody style="font-size: 15px;">
{% for dict_investigation in inv_global %}
<tr class="border-color: blue;"> <tr class="border-color: blue;">
<td> <td>
<a href="{{ url_for('investigations_b.show_investigation') }}?uuid={{ dict_investigation['uuid'] }}"> <a href="{{ url_for('investigations_b.show_investigation') }}?uuid={{ dict_investigation['uuid'] }}">

View file

@ -47,6 +47,16 @@
<th>Creator</th> <th>Creator</th>
<td>{{metadata['user_creator']}}</td> <td>{{metadata['user_creator']}}</td>
</tr> </tr>
<tr>
<th>Level</th>
<td>
{% if metadata['level'] == 1 %}
Global
{% elif metadata['level'] == 2 %}
My Organisation
{% endif %}
</td>
</tr>
<tr> <tr>
<th>Tags</th> <th>Tags</th>
<td> <td>