Skip to content

Commit 60c691d

Browse files
committed
chg: [settings + misp export] add new user config engine + Users can create and save MISP accounts/API keys to seamlessly export data to MISP or generate a JSON file
1 parent 876bc3e commit 60c691d

12 files changed

+844
-188
lines changed

bin/exporter/MISPExporter.py

+22-19
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,22 @@ def __init__(self, url='', key='', ssl=False):
8585
elif url or key:
8686
raise Exception('Error: missing url or api key')
8787
else:
88-
try:
89-
from mispKEYS import misp_url, misp_key, misp_verifycert
90-
self.url = misp_url
91-
self.key = misp_key
92-
self.ssl = misp_verifycert
93-
if self.ssl is False:
94-
urllib3_disable_warnings()
95-
if self.url.endswith('/'):
96-
self.url = self.url[:-1]
97-
except Exception: # ModuleNotFoundError
98-
self.url = None
99-
self.key = None
100-
self.ssl = None
88+
self.url = None
89+
self.key = None
90+
self.ssl = None
91+
# try:
92+
# from mispKEYS import misp_url, misp_key, misp_verifycert
93+
# self.url = misp_url
94+
# self.key = misp_key
95+
# self.ssl = misp_verifycert
96+
# if self.ssl is False:
97+
# urllib3_disable_warnings()
98+
# if self.url.endswith('/'):
99+
# self.url = self.url[:-1]
100+
# except Exception: # ModuleNotFoundError
101+
# self.url = None
102+
# self.key = None
103+
# self.ssl = None
101104

102105
def get_misp(self):
103106
try:
@@ -125,7 +128,7 @@ def ping_misp(self):
125128
@staticmethod
126129
def sanitize_distribution(distribution):
127130
try:
128-
int(distribution)
131+
distribution = int(distribution)
129132
if 0 <= distribution <= 3:
130133
return distribution
131134
else:
@@ -136,7 +139,7 @@ def sanitize_distribution(distribution):
136139
@staticmethod
137140
def sanitize_threat_level(threat_level):
138141
try:
139-
int(threat_level)
142+
threat_level = int(threat_level)
140143
if 1 <= threat_level <= 4:
141144
return threat_level
142145
else:
@@ -147,7 +150,7 @@ def sanitize_threat_level(threat_level):
147150
@staticmethod
148151
def sanitize_analysis(analysis):
149152
try:
150-
int(analysis)
153+
analysis = int(analysis)
151154
if 0 <= analysis <= 2:
152155
return analysis
153156
else:
@@ -189,7 +192,7 @@ def get_daily_event_id(self):
189192
def create_event(self, objs, export=False, event_uuid=None, date=None, publish=False, info=None, tags=None,
190193
analysis=0, distribution=0, threat_level=4):
191194
# Test Connection
192-
if export:
195+
if export and self.url:
193196
self.get_misp()
194197
if tags is None:
195198
tags = []
@@ -289,8 +292,8 @@ def export(self, investigation):
289292
tags=investigation.get_tags(),
290293
export=True)
291294
url = event['url']
292-
if url:
293-
investigation.add_misp_events(url)
295+
# if url:
296+
# investigation.add_misp_events(url)
294297
return url
295298

296299
class MISPExporterTrackerMatch(MISPExporter):

bin/lib/Investigations.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def sanityze_uuid(UUID):
5151
def generate_uuid():
5252
return str(uuid.uuid4()).replace('-', '')
5353

54-
##-- UUID --##
54+
## -- UUID -- ##
5555

5656
# status
5757
# created
@@ -195,8 +195,8 @@ def get_last_change(self, r_str=False):
195195
last_change = datetime.datetime.fromtimestamp(float(last_change)).strftime('%Y-%m-%d %H:%M:%S')
196196
return last_change
197197

198-
def get_misp_events(self):
199-
return r_tracking.smembers(f'investigations:misp:{self.uuid}')
198+
# def get_misp_events(self):
199+
# return r_tracking.smembers(f'investigations:misp:{self.uuid}')
200200

201201
# # TODO: DATE FORMAT
202202
def get_metadata(self, options=set(), r_str=False):
@@ -220,7 +220,7 @@ def get_metadata(self, options=set(), r_str=False):
220220
'last_change': self.get_last_change(r_str=r_str),
221221
'info': self.get_info(),
222222
'nb_objects': self.get_nb_objects(),
223-
'misp_events': list(self.get_misp_events())
223+
# 'misp_events': list(self.get_misp_events())
224224
}
225225
if 'objects' in options:
226226
meta['objects'] = self.get_objects()
@@ -648,7 +648,7 @@ def api_unregister_object(user_org, user_id, user_role, json_dict):
648648
res = investigation.unregister_object(obj_id, obj_type, subtype)
649649
return res, 200
650650

651-
##-- API --##
651+
## -- API -- ##
652652

653653
#
654654
# if __name__ == '__main__':

bin/lib/ail_config.py

+274
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
#!/usr/bin/python3
2+
3+
import os
4+
import sys
5+
6+
from abc import ABC
7+
8+
sys.path.append(os.environ['AIL_BIN'])
9+
##################################
10+
# Import Project packages
11+
##################################
12+
from lib import ail_users
13+
from lib.ail_core import generate_uuid5, is_valid_uuid_v5
14+
from lib.ConfigLoader import ConfigLoader
15+
16+
# Config
17+
config_loader = ConfigLoader()
18+
r_serv_db = config_loader.get_db_conn("Kvrocks_DB")
19+
r_cache = config_loader.get_redis_conn("Redis_Cache")
20+
config_loader = None
21+
22+
23+
# SIMPLE CONFIG
24+
def get_user_simple_config(user_id, name):
25+
return r_serv_db.hget(f'ail:user:settings:{user_id}', name)
26+
27+
def set_user_simple_config(user_id, name, value):
28+
return r_serv_db.hset(f'ail:user:settings:{user_id}', name, value)
29+
30+
def remove_user_simple_config(user_id, name):
31+
return r_serv_db.hdel(f'ail:user:settings:{user_id}', name)
32+
33+
# SET
34+
def get_user_set_names():
35+
return set()
36+
37+
def get_user_set_config(user_id, name):
38+
return r_serv_db.smembers(f'ail:user:set:settings:{name}:{user_id}')
39+
40+
def add_user_set_config(user_id, name, value):
41+
return r_serv_db.sadd(f'ail:user:set:settings:{name}:{user_id}', value)
42+
43+
def remove_user_set_config(user_id, name, value):
44+
return r_serv_db.srem(f'ail:user:set:settings:{name}:{user_id}', value)
45+
46+
def delete_user_set_config(user_id, name):
47+
return r_serv_db.delete(f'ail:user:set:settings:{name}:{user_id}')
48+
49+
# OBJECT
50+
def get_user_obj_names():
51+
return {'misp'}
52+
53+
def _get_user_obj_config(user_id, obj_name, field_name):
54+
field = f'{obj_name}:{field_name}'
55+
return get_user_simple_config(user_id, field)
56+
57+
def _set_user_obj_config(user_id, obj_name, field_name, value):
58+
field = f'{obj_name}:{field_name}'
59+
return set_user_simple_config(user_id, field, value)
60+
61+
def _remove_user_obj_config(user_id, obj_name, field_name):
62+
field = f'{obj_name}:{field_name}'
63+
return remove_user_simple_config(user_id, field)
64+
65+
66+
class AbstractUserConfigUniqObject(ABC):
67+
def __init__(self, obj_name, fields_names, user_id):
68+
self.name = obj_name
69+
self.fields_names = fields_names
70+
self.user_id = user_id
71+
72+
def get_config_field(self, field_name):
73+
return _get_user_obj_config(self.user_id, self.name, field_name)
74+
75+
def get_config(self):
76+
obj_config = {}
77+
for field_name in self.fields_names:
78+
conf = _get_user_obj_config(self.user_id, self.name, field_name)
79+
obj_config[field_name] = conf
80+
return obj_config
81+
82+
def set_config_field(self, field_name, value):
83+
_set_user_obj_config(self.user_id, self.name, field_name, value)
84+
85+
def set_config(self, config):
86+
obj_config = {}
87+
for field in config:
88+
field_name = field
89+
value = config[field]
90+
_set_user_obj_config(self.user_id, self.name, field_name, value)
91+
return obj_config
92+
93+
# Objects
94+
95+
class AbstractUserConfigObject(ABC):
96+
def __init__(self, name, fields_names, uuidv5, user_id):
97+
self.name = name
98+
self.fields_names = fields_names
99+
self.user_id = user_id
100+
self.uuid = uuidv5
101+
102+
def exists(self):
103+
return r_serv_db.sismember(f'ail:user:obj:settings:{self.name}:{self.user_id}', self.uuid)
104+
105+
def get_uuid(self):
106+
return self.uuid
107+
108+
def get_name(self):
109+
return self.name
110+
111+
def get_key_name(self):
112+
return f'{self.name}:{self.uuid}'
113+
114+
def get_config_field(self, field_name):
115+
return _get_user_obj_config(self.user_id, self.get_key_name(), field_name)
116+
117+
def get_config(self):
118+
obj_config = {}
119+
for field_name in self.fields_names:
120+
conf = _get_user_obj_config(self.user_id, self.get_key_name(), field_name)
121+
obj_config[field_name] = conf
122+
return obj_config
123+
124+
def set_config_field(self, field_name, value):
125+
r_serv_db.sadd(f'ail:user:obj:settings:{self.name}:{self.user_id}', self.uuid)
126+
_set_user_obj_config(self.user_id, self.get_key_name(), field_name, value)
127+
128+
def set_config(self, config):
129+
r_serv_db.sadd(f'ail:user:obj:settings:{self.name}:{self.user_id}', self.uuid)
130+
obj_config = {}
131+
for field in config:
132+
field_name = field
133+
value = config[field]
134+
_set_user_obj_config(self.user_id, self.get_key_name(), field_name, value)
135+
return obj_config
136+
137+
def delete(self):
138+
for field in self.fields_names:
139+
_remove_user_obj_config(self.user_id, self.get_key_name(), field)
140+
r_serv_db.srem(f'ail:user:obj:settings:{self.name}:{self.user_id}', self.uuid)
141+
142+
def get_user_config_objs(name, user_id):
143+
return r_serv_db.smembers(f'ail:user:obj:settings:{name}:{user_id}')
144+
145+
def delete_user_config_objs(name, user_id):
146+
r_serv_db.delete(f'ail:user:obj:settings:{name}:{user_id}')
147+
148+
def delete_user_config(user_id):
149+
for set_name in get_user_set_names():
150+
delete_user_set_config(user_id, set_name)
151+
for obj_name in get_user_obj_names():
152+
delete_user_config_objs(obj_name, user_id)
153+
r_serv_db.delete(f'ail:user:settings:{user_id}')
154+
155+
#### MISP ####
156+
157+
class UserConfigMISP(AbstractUserConfigObject):
158+
def __init__(self, uuidv5, user_id):
159+
super().__init__(name='misp', fields_names={'url', 'key', 'ssl'}, uuidv5=uuidv5, user_id=user_id)
160+
161+
def get_description(self):
162+
return self.get_config_field('description')
163+
164+
def get_url(self):
165+
return self.get_config_field('url')
166+
167+
def get_meta(self):
168+
meta = {'uuid': self.uuid,
169+
'url': self.get_url(),
170+
'key': self.get_config_field('key'),
171+
'ssl': self.get_config_field('ssl') == 'True',
172+
'description': self.get_description()}
173+
return meta
174+
175+
176+
def get_user_config_misps(user_id):
177+
meta = []
178+
for uuidv5 in get_user_config_objs('misp', user_id):
179+
misp_cong = UserConfigMISP(uuidv5, user_id)
180+
meta.append(misp_cong.get_meta())
181+
return meta
182+
183+
def create_user_config_misp(user_id, url, key, ssl, description):
184+
uuidv5 = generate_uuid5(f'{url}|{key}')
185+
misp_cong = UserConfigMISP(uuidv5, user_id)
186+
if misp_cong.exists():
187+
return uuidv5
188+
else:
189+
ssl = bool(ssl)
190+
misp_cong.set_config({'url': url, 'key': key, 'ssl': str(ssl), 'description': description})
191+
192+
def edit_user_config_misp(uuidv5, user_id, url, key, ssl, description):
193+
new_uuidv5 = generate_uuid5(f'{url}|{key}')
194+
if new_uuidv5 != uuidv5:
195+
UserConfigMISP(uuidv5, user_id).delete()
196+
return create_user_config_misp(user_id, url, key, ssl, description)
197+
else:
198+
misp_conf = UserConfigMISP(new_uuidv5, user_id)
199+
misp_conf.set_config_field('ssl', str(ssl))
200+
misp_conf.set_config_field('description', description)
201+
return new_uuidv5
202+
203+
## ## API ## ##
204+
205+
def get_user_misps_selector(user_id):
206+
misps = []
207+
for misp_uuid in get_user_config_objs('misp', user_id):
208+
misp = UserConfigMISP(misp_uuid, user_id)
209+
name = misp.get_description()
210+
if not name:
211+
misps.append(f'{misp_uuid}:{misp.get_url()}')
212+
else:
213+
misps.append(f'{misp_uuid}:{misp.get_url()} - {name}')
214+
return misps
215+
216+
def api_get_user_misps(user_id, uuidv5):
217+
misp_cong = UserConfigMISP(uuidv5, user_id)
218+
if not misp_cong.exists():
219+
return {'status': 'error', 'reason': 'Unknown uuid'}, 404
220+
else:
221+
return misp_cong.get_meta(), 200
222+
223+
def api_edit_user_misp(user_id, data):
224+
conf_uuid = data.get('uuid')
225+
url = data.get('url')
226+
key = data.get('key')
227+
misp_ssl = data.get('ssl')
228+
if misp_ssl:
229+
misp_ssl = True
230+
else:
231+
misp_ssl = False
232+
description = data.get('description')
233+
if description:
234+
if len(description) > 2000:
235+
description = description[:2000]
236+
237+
if not url:
238+
return {'status': 'error', 'reason': 'missing misp url'}, 400
239+
if not key:
240+
return {'status': 'error', 'reason': 'missing misp key'}, 400
241+
242+
if len(url) > 2000:
243+
url = url[:2000]
244+
if len(key) > 200:
245+
key = key[:200]
246+
247+
if not ail_users.exists_user(user_id):
248+
return {'status': 'error', 'reason': 'Unknown user'}, 400
249+
250+
if conf_uuid:
251+
if not is_valid_uuid_v5(conf_uuid):
252+
return {'status': 'error', 'reason': 'Unknown user'}, 400
253+
else:
254+
return edit_user_config_misp(conf_uuid, user_id, url, key, misp_ssl, description), 200
255+
else:
256+
return create_user_config_misp(user_id, url, key, misp_ssl, description), 200
257+
258+
def api_delete_user_misp(user_id, data):
259+
conf_uuid = data.get('uuid')
260+
if not is_valid_uuid_v5(conf_uuid):
261+
return {'status': 'error', 'reason': 'Unknown user'}, 400
262+
else:
263+
misp = UserConfigMISP(conf_uuid, user_id)
264+
misp.delete()
265+
return conf_uuid, 200
266+
267+
## -- API -- ##
268+
269+
## --MISP-- ##
270+
271+
272+
273+
274+

0 commit comments

Comments
 (0)