chg: [core] add users organisation + tracker acl by organisation

This commit is contained in:
terrtia 2024-08-26 15:56:46 +02:00
parent ee443ff313
commit 69471e0d37
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
11 changed files with 686 additions and 96 deletions

View file

@ -186,10 +186,13 @@ class Tracker:
def is_level_user(self):
return self.get_level() == 0
def is_level_org(self):
return self.get_level() == 2
def is_level_global(self):
return self.get_level() == 1
def _set_level(self, level, tracker_type=None, user=None):
def _set_level(self, level, tracker_type=None, org=None, user=None):
if not tracker_type:
tracker_type = self.get_type()
if level == 0: # user only
@ -200,6 +203,9 @@ class Tracker:
elif level == 1: # global
r_tracker.sadd('global:tracker', self.uuid)
r_tracker.sadd(f'global:tracker:{tracker_type}', self.uuid)
elif level == 2: # org only
r_tracker.sadd(f'org:tracker:{org}', self.uuid)
r_tracker.sadd(f'org:tracker:{org}:{tracker_type}', self.uuid)
self._set_field('level', level)
def get_filters(self):
@ -252,6 +258,9 @@ class Tracker:
def _del_mails(self):
r_tracker.delete(f'tracker:mail:{self.uuid}')
def get_org(self):
return self._get_field('org')
def get_user(self):
return self._get_field('user_id')
@ -285,6 +294,8 @@ class Tracker:
'date': self.get_date(),
'first_seen': self.get_first_seen(),
'last_seen': self.get_last_seen()}
if 'org' in options:
meta['org'] = self.get_org()
if 'user' in options:
meta['user'] = self.get_user()
if 'level' in options:
@ -391,7 +402,7 @@ class Tracker:
# TODO escape custom tags
# TODO escape mails ????
def create(self, tracker_type, to_track, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None):
def create(self, tracker_type, to_track, org, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None):
if self.exists():
raise Exception('Error: Tracker already exists')
@ -413,6 +424,7 @@ class Tracker:
self._set_field('tracked', to_track)
self._set_field('type', tracker_type)
self._set_field('date', datetime.date.today().strftime("%Y%m%d"))
self._set_field('org', org)
self._set_field('user_id', user_id)
if description:
self._set_field('description', escape(description))
@ -426,8 +438,10 @@ class Tracker:
r_tracker.sadd('trackers:all', self.uuid)
r_tracker.sadd(f'trackers:all:{tracker_type}', self.uuid)
# TRACKER LEVEL
self._set_level(level, tracker_type=tracker_type, user=user_id)
self._set_level(level, tracker_type=tracker_type, org=org, user=user_id)
# create tracker tags list
if tags:
@ -454,7 +468,7 @@ class Tracker:
trigger_trackers_refresh(tracker_type)
return self.uuid
def edit(self, tracker_type, to_track, level, description=None, filters={}, tags=[], mails=[], webhook=None):
def edit(self, tracker_type, to_track, level, org, description=None, filters={}, tags=[], mails=[], webhook=None): # TODO ADMIN: EDIT ORG UUID
# edit tracker
old_type = self.get_type()
@ -481,9 +495,14 @@ class Tracker:
# LEVEL
if old_level == 0:
r_tracker.srem(f'user:tracker:{user_id}:{old_type}', self.uuid)
r_tracker.srem(f'user:tracker:{user_id}', self.uuid)
elif old_level == 1:
r_tracker.srem(f'global:tracker:{old_type}', self.uuid)
self._set_level(level, tracker_type=tracker_type, user=user_id)
r_tracker.srem(f'global:tracker', self.uuid)
elif old_level == 2:
r_tracker.srem(f'org:tracker:{self.get_org()}:{old_type}', self.uuid)
r_tracker.srem(f'org:tracker:{self.get_org()}', self.uuid)
self._set_level(level, tracker_type=tracker_type, org=org, user=user_id)
# Delete OLD YARA Rule File
if old_type == 'yara':
if not is_default_yara_rule(old_to_track):
@ -506,11 +525,16 @@ class Tracker:
# Same Type
elif level != old_level:
if level == 0:
r_tracker.srem('global:tracker', self.uuid)
elif level == 1:
if old_level == 0:
r_tracker.srem(f'user:tracker:{user_id}', self.uuid)
self._set_level(level, tracker_type=tracker_type, user=user_id)
r_tracker.srem(f'user:tracker:{user_id}:{tracker_type}', self.uuid)
elif old_level == 1:
r_tracker.srem('global:tracker', self.uuid)
r_tracker.srem(f'global:tracker:{tracker_type}', self.uuid)
elif old_level == 2:
r_tracker.srem(f'org:tracker:{self.get_org()}', self.uuid)
r_tracker.srem(f'org:tracker:{self.get_org()}:{tracker_type}', self.uuid)
self._set_level(level, tracker_type=tracker_type, org=org, user=user_id)
# To Track Edited
if to_track != old_to_track:
@ -589,21 +613,25 @@ class Tracker:
elif level == 1: # global
r_tracker.srem('global:tracker', self.uuid)
r_tracker.srem(f'global:tracker:{tracker_type}', self.uuid)
elif level == 2: # TODO ORG check delete permission
org = self.get_org()
r_tracker.srem(f'org:tracker:{org}', self.uuid)
r_tracker.srem(f'org:tracker:{org}:{tracker_type}', self.uuid)
# meta
r_tracker.delete(f'tracker:{self.uuid}')
trigger_trackers_refresh(tracker_type)
def create_tracker(tracker_type, to_track, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None, tracker_uuid=None):
def create_tracker(tracker_type, to_track, org, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None, tracker_uuid=None):
if not tracker_uuid:
tracker_uuid = str(uuid.uuid4())
tracker = Tracker(tracker_uuid)
return tracker.create(tracker_type, to_track, user_id, level, description=description, filters=filters, tags=tags,
return tracker.create(tracker_type, to_track, org, user_id, level, description=description, filters=filters, tags=tags,
mails=mails, webhook=webhook)
def _re_create_tracker(tracker_type, tracker_uuid, to_track, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None, first_seen=None, last_seen=None):
create_tracker(tracker_type, to_track, user_id, level, description=description, filters=filters,
def _re_create_tracker(tracker_type, tracker_uuid, to_track, org, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None, first_seen=None, last_seen=None):
create_tracker(tracker_type, to_track, org, user_id, level, description=description, filters=filters,
tags=tags, mails=mails, webhook=webhook, tracker_uuid=tracker_uuid)
def get_trackers_types():
@ -649,6 +677,12 @@ def get_user_trackers(user_id, tracker_type=None):
else:
return r_tracker.smembers(f'user:tracker:{user_id}')
def get_org_trackers(org, tracker_type=None):
if tracker_type:
return r_tracker.smembers(f'org:tracker:{org}:{tracker_type}')
else:
return r_tracker.smembers(f'org:tracker:{org}')
def get_nb_global_trackers(tracker_type=None):
if tracker_type:
return r_tracker.scard(f'global:tracker:{tracker_type}')
@ -661,6 +695,13 @@ def get_nb_user_trackers(user_id, tracker_type=None):
else:
return r_tracker.scard(f'user:tracker:{user_id}')
def get_nb_org_trackers(org, tracker_type=None):
if tracker_type:
return r_tracker.scard(f'org:tracker:{org}:{tracker_type}')
else:
return r_tracker.scard(f'org:tracker:{org}')
def get_user_trackers_meta(user_id, tracker_type=None):
metas = []
for tracker_uuid in get_user_trackers(user_id, tracker_type=tracker_type):
@ -675,6 +716,13 @@ def get_global_trackers_meta(tracker_type=None):
metas.append(tracker.get_meta(options={'description', 'mails', 'sparkline', 'tags'}))
return metas
def get_org_trackers_meta(user_org, tracker_type=None):
metas = []
for tracker_uuid in get_org_trackers(user_org, tracker_type=tracker_type):
tracker = Tracker(tracker_uuid)
metas.append(tracker.get_meta(options={'description', 'mails', 'sparkline', 'tags'}))
return metas
def get_users_trackers_meta():
trackers = []
for tracker_uuid in get_trackers():
@ -683,6 +731,14 @@ def get_users_trackers_meta():
trackers.append(tracker.get_meta(options={'mails', 'sparkline', 'tags'}))
return trackers
def get_orgs_trackers_meta():
trackers = []
for tracker_uuid in get_trackers():
tracker = Tracker(tracker_uuid)
if tracker.is_level_org():
trackers.append(tracker.get_meta(options={'mails', 'sparkline', 'tags'}))
return trackers
def get_trackers_graph_by_day(l_trackers, num_day=31, date_from=None, date_to=None):
if date_from and date_to:
date_range = Date.substract_date(date_from, date_to)
@ -725,13 +781,14 @@ def get_user_dashboard(user_id): # TODO SORT + REMOVE OLDER ROWS (trim)
return trackers
def get_trackers_stats(user_id):
def get_trackers_stats(user_org, user_id):
stats = {'all': 0}
for tracker_type in get_trackers_types():
nb_global = get_nb_global_trackers(tracker_type=tracker_type)
nb_user = get_nb_user_trackers(user_id, tracker_type=tracker_type)
stats[tracker_type] = nb_global + nb_user
stats['all'] += nb_global + nb_user
nb_org = get_nb_org_trackers(user_org, tracker_type=tracker_type)
stats[tracker_type] = nb_global + nb_user + nb_org
stats['all'] += nb_global + nb_user + nb_org
return stats
@ -789,7 +846,7 @@ def api_check_tracker_uuid(tracker_uuid):
return {"status": "error", "reason": "Unknown uuid"}, 404
return None
def api_check_tracker_acl(tracker_uuid, user_id):
def api_check_tracker_acl(tracker_uuid, user_org, user_id):
res = api_check_tracker_uuid(tracker_uuid)
if res:
return res
@ -797,31 +854,45 @@ def api_check_tracker_acl(tracker_uuid, user_id):
if tracker.is_level_user():
if tracker.get_user() != user_id or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
elif tracker.is_level_org():
if tracker.get_org() != user_org or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
return None
def api_is_allowed_to_edit_tracker(tracker_uuid, user_id):
if not is_valid_uuid_v4(tracker_uuid):
return {"status": "error", "reason": "Invalid uuid"}, 400
tracker_creator = r_tracker.hget('tracker:{}'.format(tracker_uuid), 'user_id')
if not tracker_creator:
return {"status": "error", "reason": "Unknown uuid"}, 404
user = AILUser(user_id)
if not user.is_in_role('admin') and user_id != tracker_creator:
return {"status": "error", "reason": "Access Denied"}, 403
return {"uuid": tracker_uuid}, 200
def api_is_allowed_to_access_tracker(tracker_uuid, user_id):
if not is_valid_uuid_v4(tracker_uuid):
return {"status": "error", "reason": "Invalid uuid"}, 400
tracker_creator = r_tracker.hget('tracker:{}'.format(tracker_uuid), 'user_id')
if not tracker_creator:
return {"status": "error", "reason": "Unknown uuid"}, 404
user = AILUser(user_id)
if not is_tracker_global_level(tracker_uuid):
if not user.is_in_role('admin') and user_id != tracker_creator:
def api_is_allowed_to_edit_tracker(tracker_uuid, user_org, user_id):
res = api_check_tracker_uuid(tracker_uuid)
if res:
return res
tracker = Tracker(tracker_uuid)
if tracker.is_level_user():
if tracker.get_user() != user_id or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
return {"uuid": tracker_uuid}, 200
elif tracker.is_level_org():
if tracker.get_org() != user_org or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
else: # global
if tracker.get_user() != user_id or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
return None
def api_is_allowed_to_edit_tracker_level(tracker_uuid, user_org, user_id, new_level):
tracker = Tracker(tracker_uuid)
level = tracker.get_level()
if level == new_level:
return None
# Global Edit
if level == 1:
if new_level == 0:
if tracker.get_user() != user_id or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
elif new_level == 2:
if tracker.get_org() != user_org or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
# Community Edit
elif level == 2:
if new_level == 0:
if tracker.get_user() != user_id or not AILUser(user_id).is_in_role('admin'):
return {"status": "error", "reason": "Access Denied"}, 403
##-- ACL --##
@ -922,7 +993,7 @@ def api_validate_tracker_to_add(to_track, tracker_type, nb_words=1):
return {"status": "error", "reason": "Incorrect type"}, 400
return {"status": "success", "tracked": to_track, "type": tracker_type}, 200
def api_add_tracker(dict_input, user_id):
def api_add_tracker(dict_input, org, user_id):
to_track = dict_input.get('tracked', None)
if not to_track:
return {"status": "error", "reason": "Tracker not provided"}, 400
@ -982,17 +1053,17 @@ def api_add_tracker(dict_input, user_id):
level = int(level)
except TypeError:
level = 1
if level not in range(0, 1):
if level not in range(0, 3):
level = 1
tracker_uuid = create_tracker(tracker_type, to_track, user_id, level, description=description, filters=filters,
tracker_uuid = create_tracker(tracker_type, to_track, org, user_id, level, description=description, filters=filters,
tags=tags, mails=mails, webhook=webhook)
return {'tracked': to_track, 'type': tracker_type, 'uuid': tracker_uuid}, 200
def api_edit_tracker(dict_input, user_id):
def api_edit_tracker(dict_input, user_org, user_id):
tracker_uuid = dict_input.get('uuid')
res = api_check_tracker_acl(tracker_uuid, user_id)
res = api_check_tracker_acl(tracker_uuid, user_org, user_id)
if res:
return res
@ -1004,6 +1075,18 @@ def api_edit_tracker(dict_input, user_id):
tracker_type = dict_input.get('type', None)
if not tracker_type:
return {"status": "error", "reason": "Tracker type not provided"}, 400
level = dict_input.get('level', 1)
try:
level = int(level)
except TypeError:
level = 1
if level not in range(0, 3):
level = 1
res = api_is_allowed_to_edit_tracker_level(tracker_uuid, user_org, user_id, level)
if res:
return res
nb_words = dict_input.get('nb_words', 1)
description = dict_input.get('description', '')
description = escape(description)
@ -1053,31 +1136,23 @@ def api_edit_tracker(dict_input, user_id):
if subtype not in obj_subtypes:
return {"status": "error", "reason": "Invalid Tracker Object subtype"}, 400
level = dict_input.get('level', 1)
try:
level = int(level)
except TypeError:
level = 1
if level not in range(0, 1):
level = 1
tracker.edit(tracker_type, to_track, level, description=description, filters=filters,
tracker.edit(tracker_type, to_track, level, user_org, description=description, filters=filters,
tags=tags, mails=mails, webhook=webhook)
return {'tracked': to_track, 'type': tracker_type, 'uuid': tracker_uuid}, 200
def api_delete_tracker(data, user_id):
def api_delete_tracker(data, user_org, user_id):
tracker_uuid = data.get('uuid')
res = api_check_tracker_acl(tracker_uuid, user_id)
res = api_check_tracker_acl(tracker_uuid, user_org, user_id)
if res:
return res
tracker = Tracker(tracker_uuid)
return tracker.delete(), 200
def api_tracker_add_object(data, user_id):
def api_tracker_add_object(data, user_org, user_id):
tracker_uuid = data.get('uuid')
res = api_check_tracker_acl(tracker_uuid, user_id)
res = api_check_tracker_acl(tracker_uuid, user_org, user_id)
if res:
return res
tracker = Tracker(tracker_uuid)
@ -1092,9 +1167,9 @@ def api_tracker_add_object(data, user_id):
return {"status": "error", "reason": "Invalid Object"}, 400
return tracker.add(obj_type, subtype, obj_id, date=date), 200
def api_tracker_remove_object(data, user_id):
def api_tracker_remove_object(data, user_org, user_id):
tracker_uuid = data.get('uuid')
res = api_check_tracker_acl(tracker_uuid, user_id)
res = api_check_tracker_acl(tracker_uuid, user_org, user_id)
if res:
return res
@ -1216,7 +1291,6 @@ def get_tracked_yara_rules():
else:
rules[tracked] = rule
to_track[obj_type] = yara.compile(filepaths=rules)
print(to_track)
return to_track
def reload_yara_rules():

View file

@ -20,8 +20,10 @@ 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.ConfigLoader import ConfigLoader
# LOGS
access_logger = ail_logger.get_access_config()
@ -40,6 +42,145 @@ 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():
@ -266,6 +407,9 @@ def get_user_role(user_id):
def exists_user(user_id):
return r_serv_db.exists(f'ail:user:metadata:{user_id}')
def get_user_org(user_id):
return r_serv_db.hget(f'ail:user:metadata:{user_id}', 'org')
def get_user_creator(user_id):
return r_serv_db.hget(f'ail:user:metadata:{user_id}', 'creator')
@ -409,6 +553,9 @@ class AILUser(UserMixin):
def update_last_login(self):
r_serv_db.hset(f'ail:user:metadata:{self.user_id}', 'last_login', datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
def get_org(self):
return get_user_org(self.user_id)
def get_meta(self, options=set()):
meta = {'id': self.user_id}
if 'creator' in options:
@ -435,6 +582,8 @@ class AILUser(UserMixin):
meta['is_disabled'] = self.is_disabled()
if 'is_logged' in options:
meta['is_logged'] = is_user_logged(self.user_id)
if 'org' in options:
meta['org'] = self.get_org()
return meta
## SESSION ##
@ -549,6 +698,33 @@ 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'}

View file

@ -220,7 +220,7 @@ def trackers_migration():
print('TRACKERS MIGRATION...')
for tracker_uuid in old_Tracker.get_all_tracker_uuid():
meta = get_tracker_metadata(tracker_uuid)
Tracker._re_create_tracker(meta['type'], meta['uuid'], meta['tracked'], meta['user_id'], meta['level'],
Tracker._re_create_tracker(meta['type'], meta['uuid'], meta['tracked'], 'TEST_ORG', meta['user_id'], meta['level'],
tags=meta['tags'], mails=meta['mails'], description=meta['description'],
webhook=meta['webhook'], sources=meta['sources'],
first_seen=meta['first_seen'], last_seen=meta['last_seen'])

View file

@ -9,7 +9,7 @@ import os
import sys
import json
from flask import render_template, jsonify, request, Blueprint, redirect, url_for, Response, abort
from flask import render_template, jsonify, request, Blueprint, redirect, url_for, Response
from flask_login import login_required, current_user
sys.path.append('modules')
@ -67,7 +67,7 @@ def get_default_yara_rule_content():
def trackers_dashboard():
user_id = current_user.get_user_id()
trackers = Tracker.get_trackers_dashboard()
stats = Tracker.get_trackers_stats(user_id)
stats = Tracker.get_trackers_stats(current_user.get_org(), user_id)
return render_template("trackers_dashboard.html", trackers=trackers, stats=stats, bootstrap_label=bootstrap_label)
@hunters.route("/trackers/all")
@ -75,9 +75,10 @@ def trackers_dashboard():
@login_read_only
def tracked_menu():
user_id = current_user.get_user_id()
org_trackers = Tracker.get_org_trackers_meta(current_user.get_org())
user_trackers = Tracker.get_user_trackers_meta(user_id)
global_trackers = Tracker.get_global_trackers_meta()
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label)
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label)
@hunters.route("/trackers/word")
@login_required
@ -85,9 +86,10 @@ def tracked_menu():
def tracked_menu_word():
tracker_type = 'word'
user_id = current_user.get_user_id()
org_trackers = Tracker.get_org_trackers_meta(current_user.get_org(), tracker_type='word')
user_trackers = Tracker.get_user_trackers_meta(user_id, tracker_type='word')
global_trackers = Tracker.get_global_trackers_meta(tracker_type='word')
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
@hunters.route("/trackers/set")
@login_required
@ -95,9 +97,10 @@ def tracked_menu_word():
def tracked_menu_set():
tracker_type = 'set'
user_id = current_user.get_user_id()
org_trackers = Tracker.get_org_trackers_meta(current_user.get_org(), tracker_type=tracker_type)
user_trackers = Tracker.get_user_trackers_meta(user_id, tracker_type=tracker_type)
global_trackers = Tracker.get_global_trackers_meta(tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
@hunters.route("/trackers/regex")
@login_required
@ -105,9 +108,10 @@ def tracked_menu_set():
def tracked_menu_regex():
tracker_type = 'regex'
user_id = current_user.get_user_id()
org_trackers = Tracker.get_org_trackers_meta(current_user.get_org(), tracker_type=tracker_type)
user_trackers = Tracker.get_user_trackers_meta(user_id, tracker_type=tracker_type)
global_trackers = Tracker.get_global_trackers_meta(tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
@hunters.route("/trackers/yara")
@login_required
@ -115,9 +119,10 @@ def tracked_menu_regex():
def tracked_menu_yara():
tracker_type = 'yara'
user_id = current_user.get_user_id()
org_trackers = Tracker.get_org_trackers_meta(current_user.get_org(), tracker_type=tracker_type)
user_trackers = Tracker.get_user_trackers_meta(user_id, tracker_type=tracker_type)
global_trackers = Tracker.get_global_trackers_meta(tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=global_trackers, bootstrap_label=bootstrap_label, tracker_type=tracker_type)
@hunters.route("/trackers/typosquatting")
@login_required
@ -125,17 +130,19 @@ def tracked_menu_yara():
def tracked_menu_typosquatting():
tracker_type = 'typosquatting'
user_id = current_user.get_user_id()
org_trackers = Tracker.get_org_trackers_meta(current_user.get_org(), tracker_type=tracker_type)
user_trackers = Tracker.get_user_trackers_meta(user_id, tracker_type=tracker_type)
global_trackers = Tracker.get_global_trackers_meta(tracker_type=tracker_type)
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers,
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=global_trackers,
bootstrap_label=bootstrap_label, tracker_type=tracker_type)
@hunters.route("/trackers/admin")
@login_required
@login_admin
def tracked_menu_admin():
org_trackers = Tracker.get_orgs_trackers_meta()
user_trackers = Tracker.get_users_trackers_meta()
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=[],
return render_template("trackersManagement.html", user_trackers=user_trackers, org_trackers=org_trackers, global_trackers=[],
bootstrap_label=bootstrap_label)
@ -145,8 +152,8 @@ def tracked_menu_admin():
def show_tracker():
user_id = current_user.get_user_id()
tracker_uuid = request.args.get('uuid', None)
res = Tracker.api_is_allowed_to_access_tracker(tracker_uuid, user_id)
if res[1] != 200: # invalid access
res = Tracker.api_check_tracker_acl(tracker_uuid, current_user.get_org(), user_id)
if res: # invalid access
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
date_from = request.args.get('date_from')
@ -241,10 +248,7 @@ def parse_add_edit_request(request_form):
to_track = yara_default_rule
tracker_type = 'yara_default'
if level == 'on':
level = 1
else:
level = 0
level = int(level)
if mails:
mails = mails.split()
else:
@ -293,7 +297,8 @@ def add_tracked_menu():
if request.method == 'POST':
input_dict = parse_add_edit_request(request.form)
user_id = current_user.get_user_id()
res = Tracker.api_add_tracker(input_dict, user_id)
org = current_user.get_org()
res = Tracker.api_add_tracker(input_dict, org, user_id)
if res[1] == 200:
return redirect(url_for('hunters.trackers_dashboard'))
else:
@ -309,19 +314,19 @@ def add_tracked_menu():
@login_required
@login_analyst
def tracker_edit():
user_id = current_user.get_user_id()
user_org = current_user.get_org()
if request.method == 'POST':
input_dict = parse_add_edit_request(request.form)
user_id = current_user.get_user_id()
res = Tracker.api_edit_tracker(input_dict, user_id)
res = Tracker.api_edit_tracker(input_dict, user_org, user_id)
if res[1] == 200:
return redirect(url_for('hunters.show_tracker', uuid=res[0].get('uuid')))
else:
return create_json_response(res[0], res[1])
else:
user_id = current_user.get_user_id()
tracker_uuid = request.args.get('uuid', None)
res = Tracker.api_is_allowed_to_edit_tracker(tracker_uuid, user_id)
if res[1] != 200: # invalid access
res = Tracker.api_is_allowed_to_edit_tracker(tracker_uuid, user_org, user_id)
if res: # invalid access
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
tracker = Tracker.Tracker(tracker_uuid)
@ -352,7 +357,7 @@ def tracker_edit():
def tracker_delete():
user_id = current_user.get_user_id()
tracker_uuid = request.args.get('uuid')
res = Tracker.api_delete_tracker({'uuid': tracker_uuid}, user_id)
res = Tracker.api_delete_tracker({'uuid': tracker_uuid}, current_user.get_org(), user_id)
if res[1] != 200:
return create_json_response(res[0], res[1])
else:
@ -365,7 +370,7 @@ def tracker_delete():
def get_json_tracker_graph():
user_id = current_user.get_user_id()
tracker_uuid = request.args.get('uuid')
res = Tracker.api_check_tracker_acl(tracker_uuid, user_id)
res = Tracker.api_check_tracker_acl(tracker_uuid, current_user.get_org(), user_id)
if res:
return create_json_response(res[0], res[1])
@ -394,7 +399,7 @@ def tracker_object_add():
date = obj.get_date()
else:
date = request.args.get('date') # TODO check daterange
res = Tracker.api_tracker_add_object({'uuid': tracker_uuid, 'gid': object_global_id, 'date': date}, user_id)
res = Tracker.api_tracker_add_object({'uuid': tracker_uuid, 'gid': object_global_id, 'date': date}, current_user.get_org(), user_id)
if res[1] != 200:
return create_json_response(res[0], res[1])
else:
@ -410,7 +415,7 @@ def tracker_object_remove():
user_id = current_user.get_user_id()
tracker_uuid = request.args.get('uuid')
object_global_id = request.args.get('gid')
res = Tracker.api_tracker_remove_object({'uuid': tracker_uuid, 'gid': object_global_id}, user_id)
res = Tracker.api_tracker_remove_object({'uuid': tracker_uuid, 'gid': object_global_id}, current_user.get_org(), user_id)
if res[1] != 200:
return create_json_response(res[0], res[1])
else:
@ -426,8 +431,8 @@ def tracker_object_remove():
def tracker_objects():
user_id = current_user.get_user_id()
tracker_uuid = request.args.get('uuid', None)
res = Tracker.api_is_allowed_to_edit_tracker(tracker_uuid, user_id)
if res[1] != 200: # invalid access
res = Tracker.api_is_allowed_to_edit_tracker(tracker_uuid, current_user.get_org(), user_id)
if res: # invalid access
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
tracker = Tracker.Tracker(tracker_uuid)

View file

@ -303,6 +303,56 @@ def users_list():
#############################################
@settings_b.route("/settings/organisations", methods=['GET'])
@login_required
@login_admin
def organisations_list():
meta = ail_users.api_get_orgs_meta()
return render_template("orgs_list.html", meta=meta, acl_admin=True)
@settings_b.route("/settings/create_organisation", methods=['GET'])
@login_required
@login_admin
def create_organisation():
meta = {}
return render_template("create_org.html", meta=meta, error_mail=False, acl_admin=True)
@settings_b.route("/settings/create_org_post", methods=['POST'])
@login_required
@login_admin
def create_org_post():
# Admin ID
admin_id = current_user.get_user_id()
org_uuid = request.form.get('uuid')
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)
if r[1] != 200:
return create_json_response(r[0], r[1])
else:
return redirect(url_for('settings_b.organisations_list'))
# TODO check if uuid4
# TODO check name format + length
@settings_b.route("/settings/delete_org", methods=['GET'])
@login_required
@login_admin
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)
if r[1] != 200:
return create_json_response(r[0], r[1])
else:
return redirect(url_for('settings_b.organisations_list'))
#############################################
@settings_b.route("/settings/passivedns", methods=['GET'])
@login_required
@login_read_only

View file

@ -211,12 +211,20 @@
</div>
<div class="col-12 col-xl-3">
<div class="custom-control custom-switch mt-1">
<input class="custom-control-input" type="checkbox" name="level" id="id_level" {%if dict_tracker%}{%if dict_tracker['level']==1%}checked{%endif%}{%else%}checked{%endif%}>
<label class="custom-control-label" for="id_level">
<i class="fas fa-users"></i>&nbsp;Show tracker to all Users
</label>
</div>
<label class="mt-3" for="level_selector">View Level</label>
<select class="custom-select" id="level_selector" name="level">
{% if dict_tracker %}
<option value="1" {% if dict_tracker['level'] == 1 %}selected{% endif %}><i class="fas fa-users"></i> Global</option>
<option value="2" {% if dict_tracker['level'] == 2 %}selected{% endif %}><i class="fas fa-landmark"></i> My Organisation</option>
<option value="0" {% if dict_tracker['level'] == 0 %}selected{% endif %}><i class="fas fa-user"></i> My User</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>
<option value="0"><i class="fas fa-user"></i> My User</option>
{% endif %}
</select>
</div>
</div>

View file

@ -119,9 +119,11 @@
<td class="text-right"><b>Level</b></td>
<td>
{% if meta['level'] == 0 %}
Private
{% else %}
My User
{% elif meta['level'] == 1 %}
Global
{% elif meta['level'] == 2 %}
My Organisation
{% endif %}
</td>
</tr>

View file

@ -115,6 +115,78 @@
</div>
</div>
{% endif %}
{% if org_trackers %}
<div class="card my-3">
<div class="card-header">
<h5 class="card-title">Organisation {{ tracker_type }} Trackers</h5>
</div>
<div class="card-body">
<table id="table_org_trackers" class="table table-striped table-bordered">
<thead class="bg-dark text-white">
<tr>
<th>Type</th>
<th>Tracker</th>
<th>First seen</th>
<th>Last seen</th>
<th>Emails</th>
<th>sparkline</th>
</tr>
</thead>
<tbody style="font-size: 15px;">
{% for dict_uuid in org_trackers %}
<tr>
<td>{{ dict_uuid['type'] }}</td>
<td>
<span>
<a target="_blank" href="{{ url_for('hunters.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">
{% if dict_uuid['tracked'] %}
{% if dict_uuid['tracked']|length > 256 %}
{{ dict_uuid['tracked'][0:256] }}...
{% else %}
{{ dict_uuid['tracked'] }}
{% endif %}
{% endif %}
</a>
</span>
{% if dict_uuid['description'] %}
<div><i>{{ dict_uuid['description'] }}</i></div>
{% endif %}
<div>
{% for tag in dict_uuid['tags'] %}
<a href="{{ url_for('tags_ui.get_obj_by_tags') }}?object_type=item&ltags={{ tag }}">
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
</a>
{% endfor %}
</div>
</td>
<td>
{% if dict_uuid['first_seen'] %}
{{ dict_uuid['first_seen'][0:4] }}/{{ dict_uuid['first_seen'][4:6] }}/{{ dict_uuid['first_seen'][6:8] }}
{% endif %}
</td>
<td>
{% if dict_uuid['last_seen'] %}
{{ dict_uuid['last_seen'][0:4] }}/{{ dict_uuid['last_seen'][4:6] }}/{{ dict_uuid['last_seen'][6:8] }}
{% endif %}
</td>
<td>
{% for mail in dict_uuid['mails'] %}
{{ mail }}<br>
{% endfor %}
</td>
<td id="sparklines_{{ dict_uuid['uuid'] }}" style="text-align:center;"></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<div class="card my-3">
@ -203,6 +275,9 @@
{% for dict_uuid in user_trackers %}
sparkline("sparklines_{{ dict_uuid['uuid'] }}", {{ dict_uuid['sparkline'] }}, {height: 40});
{% endfor %}
{% for dict_uuid in org_trackers %}
sparkline("sparklines_{{ dict_uuid['uuid'] }}", {{ dict_uuid['sparkline'] }}, {height: 40});
{% endfor %}
{% for dict_uuid in global_trackers %}
sparkline("sparklines_{{ dict_uuid['uuid'] }}", {{ dict_uuid['sparkline'] }}, {height: 40});
{% endfor %}
@ -212,6 +287,11 @@
"iDisplayLength": 10,
"order": [[0, "desc"]]
});
$('#table_org_trackers').DataTable({
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
"iDisplayLength": 10,
"order": [[0, "desc"]]
});
$('#table_global_trackers').DataTable({
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
"iDisplayLength": 10,

View file

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>Create Org - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
<!-- Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
<!-- JS -->
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
</head>
<body>
{% include 'nav_bar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'settings/menu_sidebar.html' %}
<div class="col-12 col-lg-10" id="core_content">
<form class="form-signin" action="{{ url_for('settings_b.create_org_post')}}" autocomplete="off" method="post">
<h1 class="h3 mt-1 mb-3 text-center text-secondary">{% if meta['uuid'] %}Edit{% else %}Create{% endif %} Org</h1>
<div class="form-group">
<label for="inputUUID">UUID:</label>
<input class="form-control" type="text" id="inputUUID" name="uuid" placeholder="UUID" autocomplete="off" required>
</div>
<div class="form-group">
<label for="inputName">Name:</label>
<input class="form-control" type="text" id="inputName" name="name" placeholder="Name" autocomplete="off" required>
</div>
<div class="form-group">
<label for="inputDescription">Description:</label>
<input class="form-control" type="text" id="inputDescription" name="description" placeholder="Description - (optional)" autocomplete="off">
</div>
<button class="btn btn-lg btn-primary btn-block mt-3" type="submit">Submit</button>
</form>
</div>
</div>
</div>
</body>
<script>
$(document).ready(function(){
$("#nav_create_org").addClass("active");
$("#nav_orgs_management").removeClass("text-muted");
} );
function toggle_sidebar(){
if($('#nav_menu').is(':visible')){
$('#nav_menu').hide();
$('#side_menu').removeClass('border-right')
$('#side_menu').removeClass('col-lg-2')
$('#core_content').removeClass('col-lg-10')
}else{
$('#nav_menu').show();
$('#side_menu').addClass('border-right')
$('#side_menu').addClass('col-lg-2')
$('#core_content').addClass('col-lg-10')
}
}
</script>
</html>

View file

@ -101,6 +101,23 @@
</a>
</li>
</ul>
<h5 class="d-flex text-muted w-100 py-2" id="nav_orgs_management">
<span>Organisations</span>
</h5>
<ul class="nav flex-md-column flex-row navbar-nav justify-content-between w-100">
<li class="nav-item">
<a class="nav-link" href="{{url_for('settings_b.organisations_list')}}" id="nav_orgs_list">
<i class="fas fa-landmark"></i>
<span>Organisations List</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('settings_b.create_organisation')}}" id="nav_create_org">
<i class="fas fa-plus"></i>
<span>Create Organisation</span>
</a>
</li>
</ul>
{% endif %}
<h5 class="d-flex text-muted w-100" id="nav_doc">

View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html>
<head>
<title>Organisations - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
<!-- Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
<!-- JS -->
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
</head>
<body>
{% include 'nav_bar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'settings/menu_sidebar.html' %}
<div class="col-12 col-lg-10" id="core_content">
<h3>AIL Organisations:</h3>
<table id="tableorgs" class="table table-hover table-striped">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th>uuid</th>
<th>Description</th>
<th>Created at</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tbody_last_crawled">
{% for org in meta['orgs'] %}
<tr>
<td>{{org['name']}}</td>
<td>{{org['uuid']}}</td>
<td>{{org['description']}}</td>
<td>
{% if org['date_created'] %}
{{org['date_created']}}
{% endif %}
</td>
<td>
<div class="d-flex justify-content-start">
{# <a class="btn btn-outline-primary ml-3 px-1 py-0" href="{{ url_for('settings_b.edit_user', org_uuid=org['uuid']) }}">#}
{# <i class="fas fa-pencil-alt"></i>#}
{# </a>#}
<a class="btn btn-outline-danger ml-3 px-1 py-0" href="{{ url_for('settings_b.delete_org', uuid=org['uuid']) }}">
<i class="fas fa-trash-alt"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
<script>
$(document).ready(function(){
$("#nav_orgs_list").addClass("active");
$("#nav_orgs_management").removeClass("text-muted");
$('#tableorgs').DataTable({
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
"iDisplayLength": 10,
"order": [[ 0, "asc" ]]
});
});
function toggle_sidebar(){
if($('#nav_menu').is(':visible')){
$('#nav_menu').hide();
$('#side_menu').removeClass('border-right')
$('#side_menu').removeClass('col-lg-2')
$('#core_content').removeClass('col-lg-10')
}else{
$('#nav_menu').show();
$('#side_menu').addClass('border-right')
$('#side_menu').addClass('col-lg-2')
$('#core_content').addClass('col-lg-10')
}
}
</script>
</html>