2017-08-23 15:36:13 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import abc
|
|
|
|
import json
|
2017-08-24 19:21:52 +02:00
|
|
|
from json import JSONEncoder
|
2017-08-23 15:36:13 +02:00
|
|
|
import collections
|
2017-08-25 16:08:05 +02:00
|
|
|
import six # Remove that import when discarding python2 support.
|
2017-11-08 03:10:04 +01:00
|
|
|
import logging
|
|
|
|
|
|
|
|
logger = logging.getLogger('pymisp')
|
2017-08-23 15:36:13 +02:00
|
|
|
|
2017-08-28 19:01:53 +02:00
|
|
|
if six.PY2:
|
2017-11-08 03:10:04 +01:00
|
|
|
logger.warning("You're using python 2, it is strongly recommended to use python >=3.5")
|
2017-08-28 19:01:53 +02:00
|
|
|
|
|
|
|
|
2017-08-24 19:21:52 +02:00
|
|
|
class MISPEncode(JSONEncoder):
|
|
|
|
|
|
|
|
def default(self, obj):
|
|
|
|
if isinstance(obj, AbstractMISP):
|
|
|
|
return obj.jsonable()
|
|
|
|
return JSONEncoder.default(self, obj)
|
|
|
|
|
|
|
|
|
2017-08-23 15:36:13 +02:00
|
|
|
@six.add_metaclass(abc.ABCMeta) # Remove that line when discarding python2 support.
|
|
|
|
class AbstractMISP(collections.MutableMapping):
|
|
|
|
|
2017-09-12 16:46:06 +02:00
|
|
|
__not_jsonable = []
|
2017-08-23 15:36:13 +02:00
|
|
|
|
2017-12-12 17:34:09 +01:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(AbstractMISP, self).__init__()
|
2017-12-20 12:43:31 +01:00
|
|
|
self.edited = True
|
2017-12-12 17:34:09 +01:00
|
|
|
|
2017-09-18 16:37:55 +02:00
|
|
|
def properties(self):
|
2017-09-12 16:46:06 +02:00
|
|
|
to_return = []
|
|
|
|
for prop, value in vars(self).items():
|
|
|
|
if prop.startswith('_') or prop in self.__not_jsonable:
|
|
|
|
continue
|
|
|
|
to_return.append(prop)
|
|
|
|
return to_return
|
2017-08-23 15:36:13 +02:00
|
|
|
|
|
|
|
def from_dict(self, **kwargs):
|
2017-09-12 16:46:06 +02:00
|
|
|
for prop, value in kwargs.items():
|
|
|
|
if value is None:
|
2017-08-23 15:36:13 +02:00
|
|
|
continue
|
2017-09-12 16:46:06 +02:00
|
|
|
setattr(self, prop, value)
|
2017-12-20 12:43:31 +01:00
|
|
|
# We load an existing dictionary, marking it an not-edited
|
|
|
|
self.edited = False
|
2017-09-12 16:46:06 +02:00
|
|
|
|
|
|
|
def update_not_jsonable(self, *args):
|
|
|
|
self.__not_jsonable += args
|
|
|
|
|
|
|
|
def set_not_jsonable(self, *args):
|
|
|
|
self.__not_jsonable = args
|
2017-08-23 15:36:13 +02:00
|
|
|
|
|
|
|
def from_json(self, json_string):
|
|
|
|
"""Load a JSON string"""
|
|
|
|
self.from_dict(json.loads(json_string))
|
|
|
|
|
|
|
|
def to_dict(self):
|
|
|
|
to_return = {}
|
2017-09-18 16:37:55 +02:00
|
|
|
for attribute in self.properties():
|
2017-08-23 15:36:13 +02:00
|
|
|
val = getattr(self, attribute, None)
|
|
|
|
if val is None:
|
|
|
|
continue
|
|
|
|
to_return[attribute] = val
|
|
|
|
return to_return
|
|
|
|
|
2017-08-24 19:21:52 +02:00
|
|
|
def jsonable(self):
|
|
|
|
return self.to_dict()
|
|
|
|
|
2017-08-23 15:36:13 +02:00
|
|
|
def to_json(self):
|
2017-11-09 02:33:55 +01:00
|
|
|
return json.dumps(self, cls=MISPEncode)
|
2017-08-23 15:36:13 +02:00
|
|
|
|
|
|
|
def __getitem__(self, key):
|
2017-12-12 17:34:09 +01:00
|
|
|
try:
|
|
|
|
return getattr(self, key)
|
|
|
|
except AttributeError:
|
|
|
|
# Expected by pop and other dict-related methods
|
|
|
|
raise KeyError
|
2017-08-23 15:36:13 +02:00
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
2017-09-12 16:46:06 +02:00
|
|
|
setattr(self, key, value)
|
2017-08-23 15:36:13 +02:00
|
|
|
|
|
|
|
def __delitem__(self, key):
|
2017-09-12 16:46:06 +02:00
|
|
|
delattr(self, key)
|
2017-08-23 15:36:13 +02:00
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return iter(self.to_dict())
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self.to_dict())
|
2017-12-20 12:43:31 +01:00
|
|
|
|
|
|
|
@property
|
|
|
|
def edited(self):
|
|
|
|
return self.__edited
|
|
|
|
|
|
|
|
@edited.setter
|
|
|
|
def edited(self, val):
|
|
|
|
if isinstance(val, bool):
|
|
|
|
self.__edited = val
|
|
|
|
else:
|
|
|
|
raise Exception('edited can only be True or False')
|
|
|
|
|
|
|
|
def __setattr__(self, name, value):
|
|
|
|
if name in self.properties():
|
|
|
|
self.__edited = True
|
|
|
|
super(AbstractMISP, self).__setattr__(name, value)
|