mirror of https://github.com/CIRCL/AIL-framework
452 lines
18 KiB
Python
Executable File
452 lines
18 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*-coding:UTF-8 -*
|
|
|
|
import os
|
|
import sys
|
|
import redis
|
|
|
|
sys.path.append(os.path.join(os.environ['AIL_BIN'], 'lib/'))
|
|
import ConfigLoader
|
|
import item_basic
|
|
|
|
sys.path.append(os.path.join(os.environ['AIL_BIN'], 'packages/'))
|
|
import Date
|
|
#import Tag
|
|
|
|
config_loader = ConfigLoader.ConfigLoader()
|
|
r_serv_metadata = config_loader.get_redis_conn("ARDB_Metadata")
|
|
config_loader = None
|
|
|
|
def get_all_correlation_objects():
|
|
'''
|
|
Return a list of all correllated objects
|
|
'''
|
|
return ['domain', 'paste']
|
|
|
|
class Correlation(object):
|
|
|
|
def __init__(self, correlation_name, all_correlation_types):
|
|
self.correlation_name = correlation_name
|
|
self.all_correlation_types = all_correlation_types
|
|
|
|
def _exist_corelation_field(self, correlation_type, field_name, item_type='paste'):
|
|
if item_type=='paste':
|
|
return r_serv_metadata.exists('set_{}_{}:{}'.format(self.correlation_name, correlation_type, field_name))
|
|
else:
|
|
return r_serv_metadata.exists('set_domain_{}_{}:{}'.format(self.correlation_name, correlation_type, field_name))
|
|
|
|
def exist_correlation(self, subtype, obj_id):
|
|
res = r_serv_metadata.zscore('{}_all:{}'.format(self.correlation_name, subtype), obj_id)
|
|
if res is not None:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def _get_items(self, correlation_type, field_name):
|
|
res = r_serv_metadata.smembers('set_{}_{}:{}'.format(self.correlation_name, correlation_type, field_name))
|
|
if res:
|
|
return list(res)
|
|
else:
|
|
return []
|
|
|
|
def get_correlation_first_seen(self, subtype, obj_id, r_int=False):
|
|
res = r_serv_metadata.hget('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id), 'first_seen')
|
|
if r_int:
|
|
if res:
|
|
return int(res)
|
|
else:
|
|
return 99999999
|
|
else:
|
|
return res
|
|
|
|
def get_correlation_last_seen(self, subtype, obj_id, r_int=False):
|
|
res = r_serv_metadata.hget('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id), 'last_seen')
|
|
if r_int:
|
|
if res:
|
|
return int(res)
|
|
else:
|
|
return 0
|
|
else:
|
|
return res
|
|
|
|
def _get_metadata(self, subtype, obj_id):
|
|
meta_dict = {}
|
|
meta_dict['first_seen'] = self.get_correlation_first_seen(subtype, obj_id)
|
|
meta_dict['last_seen'] = self.get_correlation_last_seen(subtype, obj_id)
|
|
meta_dict['nb_seen'] = r_serv_metadata.scard('set_{}_{}:{}'.format(self.correlation_name, subtype, obj_id))
|
|
return meta_dict
|
|
|
|
def get_metadata(self, correlation_type, field_name, date_format='str_date'):
|
|
meta_dict = self._get_metadata(correlation_type, field_name)
|
|
if date_format == "str_date":
|
|
if meta_dict['first_seen']:
|
|
meta_dict['first_seen'] = '{}/{}/{}'.format(meta_dict['first_seen'][0:4], meta_dict['first_seen'][4:6], meta_dict['first_seen'][6:8])
|
|
if meta_dict['last_seen']:
|
|
meta_dict['last_seen'] = '{}/{}/{}'.format(meta_dict['last_seen'][0:4], meta_dict['last_seen'][4:6], meta_dict['last_seen'][6:8])
|
|
return meta_dict
|
|
|
|
def get_nb_object_seen_by_date(self, correlation_type, field_name, date_day):
|
|
nb = r_serv_metadata.hget('{}:{}:{}'.format(self.correlation_name, correlation_type, date_day), field_name)
|
|
if nb is None:
|
|
return 0
|
|
else:
|
|
return int(nb)
|
|
|
|
def get_list_nb_previous_correlation_object(self, correlation_type, field_name, numDay):
|
|
nb_previous_correlation = []
|
|
for date_day in Date.get_previous_date_list(numDay):
|
|
nb_previous_correlation.append(self.get_nb_object_seen_by_date(correlation_type, field_name, date_day))
|
|
return nb_previous_correlation
|
|
|
|
def _get_correlation_by_date(self, correlation_type, date):
|
|
return r_serv_metadata.hkeys('{}:{}:{}'.format(self.correlation_name, correlation_type, date))
|
|
|
|
def verify_correlation_field_request(self, request_dict, correlation_type, item_type='paste'):
|
|
if not request_dict:
|
|
return ({'status': 'error', 'reason': 'Malformed JSON'}, 400)
|
|
|
|
field_name = request_dict.get(correlation_type, None)
|
|
if not field_name:
|
|
return ( {'status': 'error', 'reason': 'Mandatory parameter(s) not provided'}, 400 )
|
|
if not self._exist_corelation_field(correlation_type, field_name, item_type=item_type):
|
|
return ( {'status': 'error', 'reason': 'Item not found'}, 404 )
|
|
|
|
def get_correlation(self, request_dict, correlation_type, field_name):
|
|
dict_resp = {}
|
|
|
|
if request_dict.get('items'):
|
|
dict_resp['items'] = self._get_items(correlation_type, field_name)
|
|
|
|
if request_dict.get('metadata'):
|
|
dict_resp['metadata'] = self._get_metadata(correlation_type, field_name)
|
|
|
|
dict_resp[correlation_type] = field_name
|
|
|
|
return (dict_resp, 200)
|
|
|
|
def get_all_correlations_by_subtype(self, subtype):
|
|
return r_serv_metadata.zrange(f'{self.correlation_name}_all:{subtype}', 0, -1)
|
|
|
|
def get_all_correlations_by_subtype_pagination(self, subtype, nb_elem=50, page=1):
|
|
start = (page - 1) * nb_elem
|
|
stop = start + nb_elem -1
|
|
return r_serv_metadata.zrange(f'{self.correlation_name}_all:{subtype}', start, stop)
|
|
|
|
def paginate_list(self, obj_list, nb_elem=50, page=1):
|
|
start = (page - 1) * nb_elem
|
|
stop = start + nb_elem
|
|
return obj_list[start:stop]
|
|
|
|
def get_all_correlation_types(self):
|
|
'''
|
|
Gel all correlation types
|
|
|
|
:return: A list of all the correlation types
|
|
:rtype: list
|
|
'''
|
|
return self.all_correlation_types
|
|
|
|
def is_valid_obj_subtype(self, subtype):
|
|
if subtype in self.all_correlation_types:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def get_correlation_obj_type(self):
|
|
if self.correlation_name=='pgpdump':
|
|
return 'pgp'
|
|
else:
|
|
return 'cryptocurrency'
|
|
|
|
def sanythise_correlation_types(self, correlation_types, r_boolean=False):
|
|
'''
|
|
Check if all correlation types in the list are valid.
|
|
|
|
:param correlation_types: list of correlation type
|
|
:type currency_type: list
|
|
|
|
:return: If a type is invalid, return the full list of correlation types else return the provided list
|
|
:rtype: list
|
|
'''
|
|
if correlation_types is None:
|
|
if r_boolean:
|
|
return False
|
|
else:
|
|
return self.get_all_correlation_types()
|
|
for correl in correlation_types: # # TODO: # OPTIMIZE:
|
|
if correl not in self.get_all_correlation_types():
|
|
if r_boolean:
|
|
return False
|
|
else:
|
|
return self.get_all_correlation_types()
|
|
if r_boolean:
|
|
return True
|
|
else:
|
|
return correlation_types
|
|
|
|
|
|
def _get_domain_correlation_obj(self, domain, correlation_type):
|
|
'''
|
|
Return correlation of a given domain.
|
|
|
|
:param domain: crawled domain
|
|
:type domain: str
|
|
:param correlation_type: correlation type
|
|
:type correlation_type: str
|
|
|
|
:return: a list of correlation
|
|
:rtype: list
|
|
'''
|
|
res = r_serv_metadata.smembers('domain_{}_{}:{}'.format(self.correlation_name, correlation_type, domain))
|
|
if res:
|
|
return list(res)
|
|
else:
|
|
return []
|
|
|
|
def get_domain_correlation_dict(self, domain, correlation_type=None, get_nb=False):
|
|
'''
|
|
Return all correlation of a given domain.
|
|
|
|
:param domain: crawled domain
|
|
:param correlation_type: list of correlation types
|
|
:type correlation_type: list, optional
|
|
|
|
:return: a dictionnary of all the requested correlations
|
|
:rtype: dict
|
|
'''
|
|
correlation_type = self.sanythise_correlation_types(correlation_type)
|
|
dict_correlation = {}
|
|
for correl in correlation_type:
|
|
res = self._get_domain_correlation_obj(domain, correl)
|
|
if res:
|
|
dict_correlation[correl] = res
|
|
if get_nb:
|
|
dict_correlation['nb'] = dict_correlation.get('nb', 0) + len(dict_correlation[correl])
|
|
return dict_correlation
|
|
|
|
def _get_correlation_obj_domain(self, field_name, correlation_type):
|
|
'''
|
|
Return all domains that contain this correlation.
|
|
|
|
:param domain: field name
|
|
:type domain: str
|
|
:param correlation_type: correlation type
|
|
:type correlation_type: str
|
|
|
|
:return: a list of correlation
|
|
:rtype: list
|
|
'''
|
|
res = r_serv_metadata.smembers('set_domain_{}_{}:{}'.format(self.correlation_name, correlation_type, field_name))
|
|
if res:
|
|
return list(res)
|
|
else:
|
|
return []
|
|
|
|
def get_correlation_obj_domain(self, field_name, correlation_type=None):
|
|
'''
|
|
Return all domain correlation of a given correlation_value.
|
|
|
|
:param field_name: field_name
|
|
:param correlation_type: list of correlation types
|
|
:type correlation_type: list, optional
|
|
|
|
:return: a dictionnary of all the requested correlations
|
|
:rtype: list
|
|
'''
|
|
correlation_type = self.sanythise_correlation_types(correlation_type)
|
|
for correl in correlation_type:
|
|
res = self._get_correlation_obj_domain(field_name, correl)
|
|
if res:
|
|
return res
|
|
return []
|
|
|
|
|
|
|
|
def _get_item_correlation_obj(self, item_id, correlation_type):
|
|
'''
|
|
Return correlation of a given item id.
|
|
|
|
:param item_id: item id
|
|
:type item_id: str
|
|
:param correlation_type: correlation type
|
|
:type correlation_type: str
|
|
|
|
:return: a list of correlation
|
|
:rtype: list
|
|
'''
|
|
res = r_serv_metadata.smembers('item_{}_{}:{}'.format(self.correlation_name, correlation_type, item_id))
|
|
if res:
|
|
return list(res)
|
|
else:
|
|
return []
|
|
|
|
def get_item_correlation_dict(self, item_id, correlation_type=None, get_nb=False):
|
|
'''
|
|
Return all correlation of a given item id.
|
|
|
|
:param item_id: item id
|
|
:param correlation_type: list of correlation types
|
|
:type correlation_type: list, optional
|
|
|
|
:return: a dictionnary of all the requested correlations
|
|
:rtype: dict
|
|
'''
|
|
correlation_type = self.sanythise_correlation_types(correlation_type)
|
|
dict_correlation = {}
|
|
for correl in correlation_type:
|
|
res = self._get_item_correlation_obj(item_id, correl)
|
|
if res:
|
|
dict_correlation[correl] = res
|
|
if get_nb:
|
|
dict_correlation['nb'] = dict_correlation.get('nb', 0) + len(dict_correlation[correl])
|
|
return dict_correlation
|
|
|
|
|
|
def get_correlation_all_object(self, correlation_type, correlation_value, correlation_objects=[]):
|
|
if not correlation_objects:
|
|
correlation_objects = get_all_correlation_objects()
|
|
correlation_obj = {}
|
|
for correlation_object in correlation_objects:
|
|
if correlation_object == 'paste':
|
|
res = self._get_items(correlation_type, correlation_value)
|
|
elif correlation_object == 'domain':
|
|
res = self.get_correlation_obj_domain(correlation_value, correlation_type=correlation_type)
|
|
else:
|
|
res = None
|
|
if res:
|
|
correlation_obj[correlation_object] = res
|
|
return correlation_obj
|
|
|
|
def update_correlation_daterange(self, subtype, obj_id, date):
|
|
date = int(date)
|
|
# obj_id don't exit
|
|
if not r_serv_metadata.exists('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id)):
|
|
r_serv_metadata.hset('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id), 'first_seen', date)
|
|
r_serv_metadata.hset('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id), 'last_seen', date)
|
|
else:
|
|
first_seen = self.get_correlation_last_seen(subtype, obj_id, r_int=True)
|
|
last_seen = self.get_correlation_first_seen(subtype, obj_id, r_int=True)
|
|
if date < first_seen:
|
|
r_serv_metadata.hset('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id), 'first_seen', date)
|
|
if date > last_seen:
|
|
r_serv_metadata.hset('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id), 'last_seen', date)
|
|
|
|
def save_item_correlation(self, subtype, obj_id, item_id, item_date):
|
|
self.update_correlation_daterange(subtype, obj_id, item_date)
|
|
|
|
# global set
|
|
r_serv_metadata.sadd('set_{}_{}:{}'.format(self.correlation_name, subtype, obj_id), item_id)
|
|
|
|
# daily
|
|
r_serv_metadata.hincrby('{}:{}:{}'.format(self.correlation_name, subtype, item_date), obj_id, 1)
|
|
|
|
# all type
|
|
r_serv_metadata.zincrby('{}_all:{}'.format(self.correlation_name, subtype), obj_id, 1)
|
|
|
|
## object_metadata
|
|
# item
|
|
r_serv_metadata.sadd('item_{}_{}:{}'.format(self.correlation_name, subtype, item_id), obj_id)
|
|
|
|
# domain
|
|
if item_basic.is_crawled(item_id):
|
|
domain = item_basic.get_item_domain(item_id)
|
|
self.save_domain_correlation(domain, subtype, obj_id)
|
|
|
|
def delete_item_correlation(self, subtype, obj_id, item_id, item_date):
|
|
#self.update_correlation_daterange(subtype, obj_id, item_date) update daterange ! # # TODO:
|
|
r_serv_metadata.srem('set_{}_{}:{}'.format(self.correlation_name, subtype, obj_id), item_id)
|
|
r_serv_metadata.srem('item_{}_{}:{}'.format(self.correlation_name, subtype, item_id), obj_id)
|
|
|
|
res = r_serv_metadata.hincrby('{}:{}:{}'.format(self.correlation_name, subtype, item_date), obj_id, -1)
|
|
if int(res) < 0: # remove last
|
|
r_serv_metadata.hdel('{}:{}:{}'.format(self.correlation_name, subtype, item_date), obj_id)
|
|
|
|
res = r_serv_metadata.zscore('{}_all:{}'.format(self.correlation_name, subtype), obj_id)
|
|
if int(res) > 0:
|
|
r_serv_metadata.zincrby('{}_all:{}'.format(self.correlation_name, subtype), obj_id, -1)
|
|
|
|
def save_domain_correlation(self, domain, subtype, obj_id):
|
|
r_serv_metadata.sadd('domain_{}_{}:{}'.format(self.correlation_name, subtype, domain), obj_id)
|
|
r_serv_metadata.sadd('set_domain_{}_{}:{}'.format(self.correlation_name, subtype, obj_id), domain)
|
|
|
|
def delete_domain_correlation(self, domain, subtype, obj_id):
|
|
r_serv_metadata.srem('domain_{}_{}:{}'.format(self.correlation_name, subtype, domain), obj_id)
|
|
r_serv_metadata.srem('set_domain_{}_{}:{}'.format(self.correlation_name, subtype, obj_id), domain)
|
|
|
|
def save_correlation(self, subtype, obj_id, date_range):
|
|
r_serv_metadata.zincrby('{}_all:{}'.format(self.correlation_name, subtype), obj_id, 0)
|
|
self.update_correlation_daterange(subtype, obj_id, date_range['date_from'])
|
|
if date_range['date_from'] != date_range['date_to']:
|
|
self.update_correlation_daterange(subtype, obj_id, date_range['date_to'])
|
|
return True
|
|
|
|
def save_obj_relationship(self, subtype, obj_id, obj2_type, obj2_id):
|
|
if obj2_type == 'domain':
|
|
self.save_domain_correlation(obj2_id, subtype, obj_id)
|
|
elif obj2_type == 'item':
|
|
self.save_item_correlation(subtype, obj_id, obj2_id, item_basic.get_item_date(obj2_id))
|
|
|
|
def delete_obj_relationship(self, subtype, obj_id, obj2_type, obj2_id):
|
|
if obj2_type == 'domain':
|
|
self.delete_domain_correlation(obj2_id, subtype, obj_id)
|
|
elif obj2_type == 'item':
|
|
self.delete_item_correlation(subtype, obj_id, obj2_id, item_basic.get_item_date(obj2_id))
|
|
|
|
def create_correlation(self, subtype, obj_id, obj_meta):
|
|
res = self.sanythise_correlation_types([subtype], r_boolean=True)
|
|
if not res:
|
|
print('invalid subtype')
|
|
return False
|
|
first_seen = obj_meta.get('first_seen', None)
|
|
last_seen = obj_meta.get('last_seen', None)
|
|
date_range = Date.sanitise_date_range(first_seen, last_seen, separator='', date_type='datetime')
|
|
res = self.save_correlation(subtype, obj_id, date_range)
|
|
if res and 'tags' in obj_meta:
|
|
# # TODO: handle mixed tags: taxonomies and Galaxies
|
|
pass
|
|
#Tag.api_add_obj_tags(tags=obj_meta['tags'], object_id=obj_id, object_type=self.get_correlation_obj_type())
|
|
return True
|
|
|
|
# # TODO: handle tags
|
|
def delete_correlation(self, subtype, obj_id):
|
|
res = self.sanythise_correlation_types([subtype], r_boolean=True)
|
|
if not res:
|
|
print('invalid subtype')
|
|
return False
|
|
if not self.exist_correlation(subtype, obj_id):
|
|
return False
|
|
|
|
obj_correlations = self.get_correlation_all_object(subtype, obj_id)
|
|
if 'domain' in obj_correlations:
|
|
for domain in obj_correlations['domain']:
|
|
r_serv_metadata.srem('domain_{}_{}:{}'.format(self.correlation_name, subtype, domain), obj_id)
|
|
r_serv_metadata.delete('set_domain_{}_{}:{}'.format(self.correlation_name, subtype, obj_id))
|
|
|
|
|
|
if 'paste' in obj_correlations: # TODO: handle item
|
|
for item_id in obj_correlations['paste']:
|
|
|
|
r_serv_metadata.srem('item_{}_{}:{}'.format(self.correlation_name, subtype, item_id), obj_id)
|
|
r_serv_metadata.delete('set_{}_{}:{}'.format(self.correlation_name, subtype, obj_id))
|
|
|
|
# delete daily correlation
|
|
first_seen = self.get_correlation_first_seen(subtype, obj_id)
|
|
last_seen = self.get_correlation_last_seen(subtype, obj_id)
|
|
meta_date = Date.sanitise_date_range(first_seen, last_seen)
|
|
date_range = Date.substract_date(meta_date['date_from'], meta_date['date_to'])
|
|
for date_day in date_range:
|
|
r_serv_metadata.hdel('{}:{}:{}'.format(self.correlation_name, subtype, date_day), obj_id)
|
|
|
|
r_serv_metadata.delete('{}_metadata_{}:{}'.format(self.correlation_name, subtype, obj_id))
|
|
r_serv_metadata.zrem('{}_all:{}'.format(self.correlation_name, subtype), obj_id)
|
|
|
|
return True
|
|
|
|
######## API EXPOSED ########
|
|
def api_check_objs_type(self, l_types):
|
|
for obj_type in l_types:
|
|
if not self.is_valid_obj_subtype(obj_type):
|
|
return ({"error": f"Invalid Type: {obj_type}"}, 400)
|
|
|
|
######## ########
|