Skip to content

Commit 6d7d2d4

Browse files
committed
chg: [retro hunt] add hunted objects status: viewed, done, rejected (fp)
1 parent 876da7d commit 6d7d2d4

File tree

5 files changed

+404
-19
lines changed

5 files changed

+404
-19
lines changed

bin/lib/Tracker.py

Lines changed: 161 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# -*-coding:UTF-8 -*
33
import json
44
import os
5-
import logging
65
import logging.config
76
import re
87
import sys
@@ -1827,6 +1826,12 @@ def get_meta(self, options=set()):
18271826
meta['nb_match'] = self.get_nb_match()
18281827
if 'nb_objs' in options:
18291828
meta['nb_objs'] = self.get_nb_objs()
1829+
if 'objs_stats' in options:
1830+
if 'nb_objs' in meta:
1831+
total = meta['nb_objs']
1832+
else:
1833+
total = None
1834+
meta['objs_stats'] = self.get_objs_stats(total=total)
18301835
if 'org' in options:
18311836
meta['org'] = self.get_org()
18321837
if 'org_name' in options:
@@ -1912,6 +1917,12 @@ def get_nb_objs(self):
19121917
objs[obj_type] = nb
19131918
return objs
19141919

1920+
def get_nb_total_objs(self):
1921+
nb = 0
1922+
for obj_type in get_objects_retro_hunted():
1923+
nb += self.get_nb_objs_by_type(obj_type)
1924+
return nb
1925+
19151926
def get_objs(self):
19161927
objs = []
19171928
for obj_type in get_objects_retro_hunted():
@@ -1920,20 +1931,102 @@ def get_objs(self):
19201931
objs.append((obj_type, subtype, obj_id))
19211932
return objs
19221933

1934+
def is_retro_hunted_obj(self, obj_gid):
1935+
return r_tracker.sismember(f'obj:retro_hunts:{obj_gid}', self.uuid)
1936+
19231937
def add(self, obj_type, subtype, obj_id):
19241938
# match by object type:
19251939
r_tracker.sadd(f'retro_hunt:objs:{self.uuid}:{obj_type}', f'{subtype}:{obj_id}')
19261940
# MAP object -> retro hunt
19271941
r_tracker.sadd(f'obj:retro_hunts:{obj_type}:{subtype}:{obj_id}', self.uuid)
19281942
self._incr_nb_match()
19291943

1930-
def remove(self, obj_type, subtype, obj_id):
1944+
def remove(self, obj_gid):
1945+
obj_type, subtype, obj_id = obj_gid.split(':', 2)
19311946
# match by object type:
19321947
r_tracker.srem(f'retro_hunt:objs:{self.uuid}:{obj_type}', f'{subtype}:{obj_id}')
19331948
# MAP object -> retro hunt
1934-
r_tracker.srem(f'obj:retro_hunts:{obj_type}:{subtype}:{obj_id}', self.uuid)
1949+
r_tracker.srem(f'obj:retro_hunts:{obj_gid}', self.uuid)
1950+
# obj status
1951+
self.delete_obj_status(obj_gid)
19351952
self._decr_nb_match()
19361953

1954+
def get_nb_objs_read(self):
1955+
return r_tracker.scard(f'retro_hunt:objs:read:{self.uuid}')
1956+
1957+
def get_objs_done(self):
1958+
return r_tracker.smembers(f'retro_hunt:objs:done:{self.uuid}')
1959+
1960+
def get_nb_objs_done(self):
1961+
return r_tracker.scard(f'retro_hunt:objs:done:{self.uuid}')
1962+
1963+
def get_objs_rejected(self):
1964+
return r_tracker.smembers(f'retro_hunt:objs:fp:{self.uuid}')
1965+
1966+
def get_nb_objs_rejected(self):
1967+
return r_tracker.scard(f'retro_hunt:objs:fp:{self.uuid}')
1968+
1969+
def get_objs_stats(self, total=None):
1970+
done = self.get_nb_objs_done()
1971+
fp = self.get_nb_objs_rejected()
1972+
read = self.get_nb_objs_read()
1973+
if total:
1974+
nb = 0
1975+
for nb_obj in total:
1976+
nb += total[nb_obj]
1977+
else:
1978+
nb = self.get_nb_total_objs()
1979+
unread = nb - done - fp - read
1980+
return {'done': done, 'fp': fp, 'read': read, 'unread': unread}
1981+
1982+
def is_obj_read(self, obj_gid):
1983+
return r_tracker.sismember(f'retro_hunt:objs:read:{self.uuid}', obj_gid)
1984+
1985+
def is_obj_done(self, obj_gid):
1986+
return r_tracker.sismember(f'retro_hunt:objs:done:{self.uuid}', obj_gid)
1987+
1988+
def is_obj_rejected(self, obj_gid):
1989+
return r_tracker.sismember(f'retro_hunt:objs:fp:{self.uuid}', obj_gid)
1990+
1991+
def get_obj_status(self, obj_gid):
1992+
if self.is_obj_read(obj_gid):
1993+
return 'read'
1994+
elif self.is_obj_done(obj_gid):
1995+
return 'done'
1996+
elif self.is_obj_rejected(obj_gid):
1997+
return 'rejected'
1998+
else:
1999+
return 'unread'
2000+
2001+
def obj_read(self, obj_gid):
2002+
self.obj_undone(obj_gid)
2003+
self.obj_unreject(obj_gid)
2004+
r_tracker.sadd(f'retro_hunt:objs:read:{self.uuid}', obj_gid)
2005+
2006+
def obj_unread(self, obj_gid):
2007+
r_tracker.srem(f'retro_hunt:objs:read:{self.uuid}', obj_gid)
2008+
2009+
def obj_done(self, obj_gid):
2010+
self.obj_unread(obj_gid)
2011+
self.obj_unreject(obj_gid)
2012+
r_tracker.sadd(f'retro_hunt:objs:done:{self.uuid}', obj_gid)
2013+
2014+
def obj_undone(self, obj_gid):
2015+
r_tracker.srem(f'retro_hunt:objs:done:{self.uuid}', obj_gid)
2016+
2017+
def obj_reject(self, obj_gid):
2018+
self.obj_unread(obj_gid)
2019+
self.obj_undone(obj_gid)
2020+
r_tracker.sadd(f'retro_hunt:objs:fp:{self.uuid}', obj_gid)
2021+
2022+
def obj_unreject(self, obj_gid):
2023+
r_tracker.srem(f'retro_hunt:objs:fp:{self.uuid}', obj_gid)
2024+
2025+
def delete_obj_status(self, obj_gid):
2026+
self.obj_unread(obj_gid)
2027+
self.obj_undone(obj_gid)
2028+
self.obj_unreject(obj_gid)
2029+
19372030
def create(self, org_uuid, user_id, level, name, rule, description=None, filters=[], mails=[], tags=[], timeout=30, state='pending'):
19382031
if self.exists():
19392032
raise Exception('Error: Retro Hunt Task already exists')
@@ -1976,6 +2069,10 @@ def delete_objs(self):
19762069
r_tracker.srem(f'retro_hunt:objs:{self.uuid}:{obj_type}', f'{subtype}:{obj_id}')
19772070
# MAP object -> retro hunt
19782071
r_tracker.srem(f'obj:retro_hunts:{obj_type}:{subtype}:{obj_id}', self.uuid)
2072+
# objs status
2073+
r_tracker.delete(f'retro_hunts:objs:read:{self.uuid}')
2074+
r_tracker.delete(f'retro_hunts:objs:done:{self.uuid}')
2075+
r_tracker.delete(f'retro_hunts:objs:fp:{self.uuid}')
19792076

19802077
def delete(self):
19812078
if self.is_running() and self.get_state() not in ['completed', 'paused']:
@@ -2092,13 +2189,13 @@ def get_retro_hunt_metas(trackers_uuid):
20922189
def is_obj_retro_hunted(obj_type, subtype, obj_id):
20932190
return r_tracker.exists(f'obj:retro_hunts:{obj_type}:{subtype}:{obj_id}')
20942191

2095-
def get_obj_retro_hunts(obj_type, subtype, obj_id):
2096-
return r_tracker.smembers(f'obj:retro_hunts:{obj_type}:{subtype}:{obj_id}')
2192+
def get_obj_retro_hunts(obj_gid):
2193+
return r_tracker.smembers(f'obj:retro_hunts:{obj_gid}')
20972194

2098-
def delete_obj_retro_hunts(obj_type, subtype, obj_id):
2099-
for retro_uuid in get_obj_retro_hunts(obj_type, subtype, obj_id):
2195+
def delete_obj_retro_hunts(obj_gid):
2196+
for retro_uuid in get_obj_retro_hunts(obj_gid):
21002197
retro_hunt = RetroHunt(retro_uuid)
2101-
retro_hunt.remove(obj_type, subtype, obj_id)
2198+
retro_hunt.remove(obj_gid)
21022199

21032200
#### ACL ####
21042201

@@ -2153,7 +2250,7 @@ def api_validate_rule_to_add(rule, rule_type):
21532250
if rule_type == 'yara_custom':
21542251
valid_yara_rule, error = is_valid_yara_rule(rule)
21552252
if not valid_yara_rule:
2156-
return {"status": "error", "reason": f"Invalid Yara Rule: {e}"}, 400
2253+
return {"status": "error", "reason": f"Invalid Yara Rule: {error}"}, 400
21572254
elif rule_type == 'yara_default':
21582255
if not is_valid_default_yara_rule(rule):
21592256
return {"status": "error", "reason": "The Yara Rule doesn't exist"}, 400
@@ -2256,6 +2353,61 @@ def api_delete_retro_hunt_task(user_org, user_id, user_role, task_uuid):
22562353
else:
22572354
return retro_hunt.delete(), 200
22582355

2356+
def api_retro_hunt_object_status_done(data, user_org, user_id, user_role):
2357+
retro_uuid = data.get('uuid')
2358+
res = api_check_retro_hunt_acl(retro_uuid, user_org, user_id, user_role, 'edit')
2359+
if res:
2360+
return res
2361+
retro_hunt = RetroHunt(retro_uuid)
2362+
object_gid = data.get('gid')
2363+
if not retro_hunt.is_retro_hunted_obj(object_gid):
2364+
return {"status": "error", "reason": "Not Retro Hunted Object"}, 404
2365+
return retro_hunt.obj_done(object_gid), 200
2366+
2367+
def api_retro_hunt_object_status_reject(data, user_org, user_id, user_role):
2368+
retro_uuid = data.get('uuid')
2369+
res = api_check_retro_hunt_acl(retro_uuid, user_org, user_id, user_role, 'edit')
2370+
if res:
2371+
return res
2372+
retro_hunt = RetroHunt(retro_uuid)
2373+
object_gid = data.get('gid')
2374+
if not retro_hunt.is_retro_hunted_obj(object_gid):
2375+
return {"status": "error", "reason": "Not Retro Hunted Object"}, 404
2376+
return retro_hunt.obj_reject(object_gid), 200
2377+
2378+
def api_retro_hunt_object_status_unread(data, user_org, user_id, user_role):
2379+
retro_uuid = data.get('uuid')
2380+
res = api_check_retro_hunt_acl(retro_uuid, user_org, user_id, user_role, 'edit')
2381+
if res:
2382+
return res
2383+
retro_hunt = RetroHunt(retro_uuid)
2384+
object_gid = data.get('gid')
2385+
if not retro_hunt.is_retro_hunted_obj(object_gid):
2386+
return {"status": "error", "reason": "Not Retro Hunted Object"}, 404
2387+
return retro_hunt.delete_obj_status(object_gid), 200
2388+
2389+
def api_retro_hunt_object_status_read(data, user_org, user_id, user_role):
2390+
retro_uuid = data.get('uuid')
2391+
res = api_check_retro_hunt_acl(retro_uuid, user_org, user_id, user_role, 'edit')
2392+
if res:
2393+
return res
2394+
retro_hunt = RetroHunt(retro_uuid)
2395+
object_gid = data.get('gid')
2396+
if not retro_hunt.is_retro_hunted_obj(object_gid):
2397+
return {"status": "error", "reason": "Not Retro Hunted Object"}, 404
2398+
return retro_hunt.obj_read(object_gid), 200
2399+
2400+
def api_retro_hunt_remove_object(data, user_org, user_id, user_role):
2401+
retro_uuid = data.get('uuid')
2402+
res = api_check_retro_hunt_acl(retro_uuid, user_org, user_id, user_role, 'edit')
2403+
if res:
2404+
return res
2405+
retro_hunt = RetroHunt(retro_uuid)
2406+
object_gid = data.get('gid')
2407+
if not retro_hunt.is_retro_hunted_obj(object_gid):
2408+
return {"status": "error", "reason": "Not Retro Hunted Object"}, 404
2409+
return retro_hunt.remove(object_gid), 200
2410+
22592411
################################################################################
22602412
################################################################################
22612413
################################################################################

bin/lib/module_extractor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def get_tracker_match(user_org, user_id, obj, content, priority=None, match_uuid
228228

229229
else:
230230
trackers_uuids = Tracker.get_obj_trackers(obj.type, obj.get_subtype(r_str=True), obj.id)
231-
retro_hunts_uuids = Tracker.get_obj_retro_hunts(obj.type, obj.get_subtype(r_str=True), obj.id)
231+
retro_hunts_uuids = Tracker.get_obj_retro_hunts(obj_gid)
232232

233233
# check if priority is tracker or retro
234234
if priority:

var/www/blueprints/hunters.py

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -664,14 +664,19 @@ def retro_hunt_show_task():
664664
if res:
665665
return res
666666

667-
dict_task = retro_hunt.get_meta(options={'creator', 'date', 'description', 'level', 'org', 'org_name', 'progress', 'filters', 'nb_objs', 'tags'})
667+
dict_task = retro_hunt.get_meta(options={'creator', 'date', 'description', 'level', 'org', 'org_name', 'progress', 'filters', 'nb_objs', 'objs_stats','tags'})
668668
rule_content = Tracker.get_yara_rule_content(dict_task['rule'])
669669
dict_task['filters'] = json.dumps(dict_task['filters'], indent=4)
670670

671+
dict_task['objs'] = []
671672
if objs:
672-
dict_task['objs'] = ail_objects.get_objects_meta(retro_hunt.get_objs(), options={'last_full_date'}, flask_context=True)
673-
else:
674-
dict_task['objs'] = []
673+
options = {'last_full_date', 'pdf'}
674+
for ob in retro_hunt.get_objs():
675+
obj_type, obj_subtype, obj_id = ob
676+
obj_meta = ail_objects.get_object_meta(obj_type, obj_subtype, obj_id, options=options, flask_context=True)
677+
obj_meta['gid'] = f'{obj_type}:{obj_subtype}:{obj_id}'
678+
obj_meta['tracker_status'] = retro_hunt.get_obj_status(obj_meta['gid'])
679+
dict_task['objs'].append(obj_meta)
675680

676681
return render_template("show_retro_hunt.html", dict_task=dict_task,
677682
rule_content=rule_content,
@@ -885,4 +890,93 @@ def retro_hunt_objects_report():
885890
chats=chats, messages=messages, bootstrap_label=bootstrap_label, force_full_image=True)
886891

887892

893+
@hunters.route('/retro_hunt/object/status/done', methods=['GET'])
894+
@login_required
895+
@login_user_no_api
896+
def retro_hunt_object_status_done():
897+
user_id = current_user.get_user_id()
898+
user_org = current_user.get_org()
899+
user_role = current_user.get_role()
900+
retro_uuid = request.args.get('uuid')
901+
object_global_id = request.args.get('gid')
902+
903+
res = Tracker.api_retro_hunt_object_status_done({'uuid': retro_uuid, 'gid': object_global_id}, user_org, user_id, user_role)
904+
if res[1] != 200:
905+
return create_json_response(res[0], res[1])
906+
else:
907+
if request.referrer:
908+
return redirect(request.referrer)
909+
else:
910+
return redirect(url_for('hunters.retro_hunt_show_task', uuid=retro_uuid))
911+
912+
@hunters.route('/retro_hunt/object/status/unread', methods=['GET'])
913+
@login_required
914+
@login_user_no_api
915+
def retro_hunt_object_status_unread():
916+
user_id = current_user.get_user_id()
917+
user_org = current_user.get_org()
918+
user_role = current_user.get_role()
919+
retro_uuid = request.args.get('uuid')
920+
object_global_id = request.args.get('gid')
921+
922+
res = Tracker.api_retro_hunt_object_status_unread({'uuid': retro_uuid, 'gid': object_global_id}, user_org, user_id, user_role)
923+
if res[1] != 200:
924+
return create_json_response(res[0], res[1])
925+
else:
926+
if request.referrer:
927+
return redirect(request.referrer)
928+
else:
929+
return redirect(url_for('hunters.retro_hunt_show_task', uuid=retro_uuid))
930+
931+
932+
@hunters.route('/retro_hunt/object/status/reject', methods=['GET'])
933+
@login_required
934+
@login_user_no_api
935+
def retro_hunt_object_status_reject():
936+
user_id = current_user.get_user_id()
937+
user_org = current_user.get_org()
938+
user_role = current_user.get_role()
939+
retro_uuid = request.args.get('uuid')
940+
object_global_id = request.args.get('gid')
941+
942+
res = Tracker.api_retro_hunt_object_status_reject({'uuid': retro_uuid, 'gid': object_global_id}, user_org, user_id, user_role)
943+
if res[1] != 200:
944+
return create_json_response(res[0], res[1])
945+
else:
946+
if request.referrer:
947+
return redirect(request.referrer)
948+
else:
949+
return redirect(url_for('hunters.retro_hunt_show_task', uuid=retro_uuid))
950+
951+
@hunters.route('/retro_hunt/object/status/read', methods=['POST'])
952+
@login_required
953+
@login_user_no_api
954+
def retro_hunt_object_status_read():
955+
user_id = current_user.get_user_id()
956+
user_org = current_user.get_org()
957+
user_role = current_user.get_role()
958+
retro_uuid = request.args.get('uuid')
959+
object_global_id = request.args.get('gid')
960+
961+
res = Tracker.api_retro_hunt_object_status_read({'uuid': retro_uuid, 'gid': object_global_id}, user_org, user_id, user_role)
962+
return create_json_response(res[0], res[1])
963+
964+
@hunters.route('/retro_hunt/object/remove', methods=['GET'])
965+
@login_required
966+
@login_user_no_api
967+
def retro_hunt_object_remove():
968+
user_id = current_user.get_user_id()
969+
user_org = current_user.get_org()
970+
user_role = current_user.get_role()
971+
retro_uuid = request.args.get('uuid')
972+
object_global_id = request.args.get('gid')
973+
res = Tracker.api_retro_hunt_remove_object({'uuid': retro_uuid, 'gid': object_global_id}, user_org, user_id, user_role)
974+
if res[1] != 200:
975+
return create_json_response(res[0], res[1])
976+
else:
977+
if request.referrer:
978+
return redirect(request.referrer)
979+
else:
980+
return redirect(url_for('hunters.retro_hunt_show_task', uuid=retro_uuid))
981+
888982
## - - ##

0 commit comments

Comments
 (0)