From ff59343bb7e8b2517700d470b59433f4bb5bf0be Mon Sep 17 00:00:00 2001 From: terrtia Date: Tue, 27 Aug 2024 15:48:11 +0200 Subject: [PATCH] chg: [ail orgs] refactor organisations + add org level to investigations --- bin/lib/Investigations.py | 157 +++++++++++-- bin/lib/ail_api.py | 4 + bin/lib/ail_orgs.py | 222 ++++++++++++++++++ bin/lib/ail_users.py | 171 +------------- update/v5.0/DB_KVROCKS_MIGRATION.py | 4 +- update/v5.7/Update.py | 14 +- var/www/blueprints/api_rest.py | 10 +- var/www/blueprints/investigations_b.py | 44 +++- var/www/blueprints/settings_b.py | 7 +- .../investigations/add_investigation.html | 13 + .../investigations/investigations.html | 40 +++- .../investigations/view_investigation.html | 10 + 12 files changed, 494 insertions(+), 202 deletions(-) create mode 100755 bin/lib/ail_orgs.py diff --git a/bin/lib/Investigations.py b/bin/lib/Investigations.py index ede905c8..c827fbee 100755 --- a/bin/lib/Investigations.py +++ b/bin/lib/Investigations.py @@ -23,6 +23,7 @@ sys.path.append(os.environ['AIL_BIN']) # Import Project packages ################################## from lib import ail_core +from lib import ail_orgs from lib import ConfigLoader from lib import Tag from lib.exceptions import UpdateInvestigationError @@ -47,9 +48,6 @@ def sanityze_uuid(UUID): sanityzed_uuid = uuid.UUID(hex=UUID, version=4) 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(): return str(uuid.uuid4()).replace('-', '') @@ -105,6 +103,42 @@ class Investigation(object): def get_name(self): 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): try: return int(r_tracking.hget(f'investigations:data:{self.uuid}', 'threat_level')) @@ -166,6 +200,8 @@ class Investigation(object): 'analysis': analysis, 'tags': list(self.get_tags()), 'user_creator': self.get_creator_user(), + 'level': self.get_level(), + 'org': self.get_org(), 'date': self.get_date(), 'timestamp': self.get_timestamp(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) 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 r_tracking.srem(f'investigations:user:{self.get_creator_user()}', self.uuid) # metadata r_tracking.delete(f'investigations:data:{self.uuid}') r_tracking.delete(f'investigations:tags:{self.uuid}') r_tracking.delete(f'investigations:misp:{self.uuid}') + return self.uuid -##-- Class --## +## -- -- ## def get_all_investigations(): return r_tracking.smembers('investigations:all') @@ -285,6 +329,12 @@ def get_all_investigations(): def exists_investigation(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 def get_user_all_investigations(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 -def _re_create_investagation(investigation_uuid, user_id, 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) +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_org, user_id, level, date, name, threat_level, analysis, info, tags=tags, investigation_uuid=investigation_uuid) if timestamp: _set_timestamp(investigation_uuid, timestamp) 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: sanitize tags # # 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 not is_valid_uuid_v4(investigation_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() r_tracking.sadd('investigations:all', investigation_uuid) # 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 + r_tracking.hset(f'investigations:data:{investigation_uuid}', 'creator_org', user_org) r_tracking.hset(f'investigations:data:{investigation_uuid}', 'creator_user', user_id) # TODO: limit info + name investigation = Investigation(investigation_uuid) + investigation.set_level(level, user_org) + investigation.set_info(info) #investigation.set_name(name) ############################################## 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)) 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 = [] - 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) name = investigation.get_info() l_investigations.append({"id": investigation_uuid, "name": name}) 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 #### -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) if not investigation.exists(): 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) # objs = [] @@ -392,6 +483,7 @@ def api_get_investigation(investigation_uuid): # TODO check if is UUIDv4 # # TODO: SANITYZE Fields # # TODO: Name ????? def api_add_investigation(json_dict): + user_org = json_dict.get('user_org') user_id = json_dict.get('user_id') name = json_dict.get('name') ##### mandatory ? name = escape(name) @@ -401,6 +493,14 @@ def api_add_investigation(json_dict): # # TODO: sanityze 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 = escape(info) info = info[:1000] @@ -409,13 +509,13 @@ def api_add_investigation(json_dict): return {"status": "error", "reason": "Invalid/Disabled tags"}, 400 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: return e.message, 400 return res, 200 # # 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(' ', '') if not is_valid_uuid_v4(investigation_uuid): return {"status": "error", "reason": "Invalid Investigation uuid"}, 400 @@ -423,6 +523,9 @@ def api_edit_investigation(json_dict): if not exists_investigation(investigation_uuid): return {"status": "error", "reason": "Investigation not found"}, 404 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 = escape(name) @@ -445,6 +548,17 @@ def api_edit_investigation(json_dict): if not Tag.are_enabled_tags(tags): 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_name(name) investigation.set_tags(tags) @@ -454,7 +568,7 @@ def api_edit_investigation(json_dict): 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(' ', '') if not is_valid_uuid_v4(investigation_uuid): return {"status": "error", "reason": "Invalid Investigation uuid"}, 400 @@ -462,10 +576,13 @@ def api_delete_investigation(json_dict): if not exists_investigation(investigation_uuid): return {"status": "error", "reason": "Investigation not found"}, 404 investigation = Investigation(investigation_uuid) + res = api_check_access_acl(investigation, user_org, is_admin=is_admin) + if res: + return res res = investigation.delete() 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(' ', '') if not is_valid_uuid_v4(investigation_uuid): 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): return {"status": "error", "reason": f"Investigation not found: {investigation_uuid}"}, 404 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(' ', '') 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) 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(' ', '') if not is_valid_uuid_v4(investigation_uuid): 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): return {"status": "error", "reason": f"Investigation not found: {investigation_uuid}"}, 404 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(' ', '') subtype = json_dict.get('subtype', '') diff --git a/bin/lib/ail_api.py b/bin/lib/ail_api.py index 58915088..d01de0fc 100755 --- a/bin/lib/ail_api.py +++ b/bin/lib/ail_api.py @@ -26,6 +26,10 @@ def is_valid_token(token): def get_user_from_token(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 # User without API if role == 'user_no_api': diff --git a/bin/lib/ail_orgs.py b/bin/lib/ail_orgs.py new file mode 100755 index 00000000..c958bcf8 --- /dev/null +++ b/bin/lib/ail_orgs.py @@ -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) diff --git a/bin/lib/ail_users.py b/bin/lib/ail_users.py index b39d2adf..ced71619 100755 --- a/bin/lib/ail_users.py +++ b/bin/lib/ail_users.py @@ -20,7 +20,7 @@ sys.path.append(os.environ['AIL_BIN']) # Import Project packages ################################## from lib import ail_logger -from lib.ail_core import is_valid_uuid_v4 +from lib import ail_orgs 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 = 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 #### def get_sessions(): @@ -680,6 +541,9 @@ class AILUser(UserMixin): else: return False + def is_admin(self): + return self.is_in_role('admin') + def get_role(self): return r_serv_db.hget(f'ail:user:metadata:{self.user_id}', 'role') @@ -698,33 +562,6 @@ class AILUser(UserMixin): #### 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(): meta = {'users': []} options = {'api_key', 'creator', 'created_at', 'is_logged', 'last_edit', 'last_login', 'last_seen', 'last_seen_api', 'role', '2fa', 'otp_setup'} diff --git a/update/v5.0/DB_KVROCKS_MIGRATION.py b/update/v5.0/DB_KVROCKS_MIGRATION.py index 3d87bb5d..f3c8e4e4 100755 --- a/update/v5.0/DB_KVROCKS_MIGRATION.py +++ b/update/v5.0/DB_KVROCKS_MIGRATION.py @@ -9,6 +9,8 @@ import sys import importlib.util +from lib.ail_core import get_ail_uuid + sys.path.append(os.environ['AIL_BIN']) ################################## # Import Project packages @@ -260,7 +262,7 @@ def investigations_migration(): for investigation_uuid in old_Investigations.get_all_investigations(): old_investigation = old_Investigations.Investigation(investigation_uuid) 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']) for dict_obj in old_investigation.get_objects(): new_investigation.register_object(dict_obj['id'], dict_obj['type'], dict_obj['subtype']) diff --git a/update/v5.7/Update.py b/update/v5.7/Update.py index 54574188..7e7d52cc 100755 --- a/update/v5.7/Update.py +++ b/update/v5.7/Update.py @@ -12,6 +12,7 @@ sys.path.append(os.environ['AIL_HOME']) ################################## from update.bin.ail_updater import AIL_Updater from lib import ail_users +from lib import Investigations from lib.ConfigLoader import ConfigLoader from lib import chats_viewer @@ -28,11 +29,22 @@ if __name__ == '__main__': config_loader = None 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}', 'created_at', 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() updater = Updater('v5.7') diff --git a/var/www/blueprints/api_rest.py b/var/www/blueprints/api_rest.py index 30009c03..cf2f9293 100644 --- a/var/www/blueprints/api_rest.py +++ b/var/www/blueprints/api_rest.py @@ -19,6 +19,7 @@ sys.path.append(os.environ['AIL_BIN']) from lib import ail_api from lib import ail_core from lib import ail_updates +from lib import ail_logger from lib import crawlers from lib import chats_viewer @@ -32,6 +33,10 @@ from lib.objects import Titles from importer.FeederImporter import api_add_json_feeder_to_queue +# LOGS +# access_logger = ail_logger.get_access_config() + + # ============ BLUEPRINT ============ 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) else: # User Authenticated + In Role + # print(funct.__name__) return funct(*args, **kwargs) else: return create_json_response({'status': 'error', 'reason': 'Internal'}, 400) @@ -245,7 +251,9 @@ def objects_titles_download_unsafe(): @api_rest.route("api/v1/investigation/", methods=['GET']) # TODO options @token_required('read_only') 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]) # TODO CATCH REDIRECT diff --git a/var/www/blueprints/investigations_b.py b/var/www/blueprints/investigations_b.py index e7666689..6208d2a1 100644 --- a/var/www/blueprints/investigations_b.py +++ b/var/www/blueprints/investigations_b.py @@ -42,17 +42,27 @@ def create_json_response(data, status_code): @login_required @login_read_only 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, - investigations=investigations) + inv_global=inv_global, inv_org=inv_org) @investigations_b.route("/investigation", methods=['GET']) ## FIXME: add /view ???? @login_required @login_read_only 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 = 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) objs = [] for obj in investigation.get_objects(): @@ -71,6 +81,8 @@ def show_investigation(): def add_investigation(): if request.method == 'POST': 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") date = request.form.get("investigation_date") threat_level = request.form.get("threat_level") @@ -93,7 +105,7 @@ def add_investigation(): 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, "analysis": analysis, "info": info, "tags": tags} res = Investigations.api_add_investigation(input_dict) @@ -108,10 +120,13 @@ def add_investigation(): @investigations_b.route("/investigation/edit", methods=['GET', 'POST']) @login_required @login_analyst -def edit_investigation(): +def edit_investigation(): # TODO CHECK ACL if request.method == 'POST': + user_org = current_user.get_org() user_id = current_user.get_user_id() + is_admin = current_user.is_admin() investigation_uuid = request.form.get("investigation_uuid") + level = request.form.get("investigation_level") name = request.form.get("investigation_name") date = request.form.get("investigation_date") threat_level = request.form.get("threat_level") @@ -135,10 +150,10 @@ def edit_investigation(): 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, "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: return create_json_response(res[0], res[1]) @@ -158,9 +173,12 @@ def edit_investigation(): @login_required @login_analyst 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') 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: return create_json_response(res[0], res[1]) return redirect(url_for('investigations_b.investigations_dashboard')) @@ -169,6 +187,9 @@ def delete_investigation(): @login_required @login_read_only 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 = investigations_uuid.split(',') @@ -182,7 +203,7 @@ def register_investigation(): "type": object_type, "subtype": object_subtype} if 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: return create_json_response(res[0], res[1]) return redirect(url_for('investigations_b.investigations_dashboard', uuid=investigation_uuid)) @@ -191,13 +212,16 @@ def register_investigation(): @login_required @login_read_only 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') object_type = request.args.get('type') object_subtype = request.args.get('subtype') object_id = request.args.get('id') input_dict = {"uuid": investigation_uuid, "id": object_id, "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: return create_json_response(res[0], res[1]) return redirect(url_for('investigations_b.show_investigation', uuid=investigation_uuid)) @@ -207,7 +231,7 @@ def unregister_investigation(): @login_required @login_read_only 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") @login_required diff --git a/var/www/blueprints/settings_b.py b/var/www/blueprints/settings_b.py index 5e56d77f..9b9cf445 100644 --- a/var/www/blueprints/settings_b.py +++ b/var/www/blueprints/settings_b.py @@ -20,6 +20,7 @@ sys.path.append(os.environ['AIL_BIN']) # Import Project packages ################################## from lib import ail_updates +from lib import ail_orgs from lib import ail_users from lib import d4 from packages import git_status @@ -307,7 +308,7 @@ def users_list(): @login_required @login_admin 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) @settings_b.route("/settings/create_organisation", methods=['GET']) @@ -328,7 +329,7 @@ def create_org_post(): name = request.form.get('name') 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: return create_json_response(r[0], r[1]) else: @@ -344,7 +345,7 @@ def create_org_post(): def delete_org(): admin_id = current_user.get_user_id() 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: return create_json_response(r[0], r[1]) else: diff --git a/var/www/templates/investigations/add_investigation.html b/var/www/templates/investigations/add_investigation.html index 3184a0e4..98d19336 100644 --- a/var/www/templates/investigations/add_investigation.html +++ b/var/www/templates/investigations/add_investigation.html @@ -144,6 +144,19 @@ {% else %} Create a new Investigation {% endif %} + +
+ + + diff --git a/var/www/templates/investigations/investigations.html b/var/www/templates/investigations/investigations.html index a15eea15..87082326 100644 --- a/var/www/templates/investigations/investigations.html +++ b/var/www/templates/investigations/investigations.html @@ -31,7 +31,7 @@

- Investigations: + Organisation Investigations:

@@ -45,7 +45,43 @@ - {% for dict_investigation in investigations %} + {% for dict_investigation in inv_org %} + + + + + + + + {% endfor %} + +
+ + {{ dict_investigation['info']}} +
+ {% for tag in dict_investigation['tags'] %} + {{ tag }} + {% endfor %} +
+
+
{{ dict_investigation['date']}}{{ dict_investigation['last_change']}}{{ dict_investigation['info']}}{{ dict_investigation['nb_objects']}}
+ +

+ Global Investigations: +

+ + + + + + + + + + + + + {% for dict_investigation in inv_global %} + + + +
NameDatelast modifiedInfoNb Objects
diff --git a/var/www/templates/investigations/view_investigation.html b/var/www/templates/investigations/view_investigation.html index 3848b736..c3143359 100644 --- a/var/www/templates/investigations/view_investigation.html +++ b/var/www/templates/investigations/view_investigation.html @@ -47,6 +47,16 @@ Creator {{metadata['user_creator']}}
Level + {% if metadata['level'] == 1 %} + Global + {% elif metadata['level'] == 2 %} + My Organisation + {% endif %} +
Tags