dictature 0.9.3__py3-none-any.whl → 0.9.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dictature/backend/directory.py +5 -0
- dictature/backend/misp.py +126 -0
- dictature/backend/mock.py +38 -0
- dictature/backend/sqlite.py +4 -1
- dictature/dictature.py +129 -1
- dictature/transformer/aes.py +6 -0
- dictature/transformer/hmac.py +4 -0
- dictature/transformer/mock.py +16 -0
- dictature/transformer/passthrough.py +7 -0
- dictature/transformer/pipeline.py +4 -0
- {dictature-0.9.3.dist-info → dictature-0.9.4.dist-info}/METADATA +8 -1
- dictature-0.9.4.dist-info/RECORD +18 -0
- dictature-0.9.3.dist-info/RECORD +0 -17
- {dictature-0.9.3.dist-info → dictature-0.9.4.dist-info}/LICENSE +0 -0
- {dictature-0.9.3.dist-info → dictature-0.9.4.dist-info}/WHEEL +0 -0
- {dictature-0.9.3.dist-info → dictature-0.9.4.dist-info}/top_level.txt +0 -0
dictature/backend/directory.py
CHANGED
@@ -9,6 +9,11 @@ from .mock import DictatureTableMock, DictatureBackendMock, Value, ValueMode
|
|
9
9
|
|
10
10
|
class DictatureBackendDirectory(DictatureBackendMock):
|
11
11
|
def __init__(self, directory: Union[Path, str], dir_prefix: str = 'db_') -> None:
|
12
|
+
"""
|
13
|
+
Create a new directory backend
|
14
|
+
:param directory: directory to store the data
|
15
|
+
:param dir_prefix: prefix for the directories of the tables
|
16
|
+
"""
|
12
17
|
if isinstance(directory, str):
|
13
18
|
directory = Path(directory)
|
14
19
|
self.__directory = directory
|
@@ -0,0 +1,126 @@
|
|
1
|
+
from json import dumps, loads
|
2
|
+
from re import sub
|
3
|
+
from typing import Iterable, Optional
|
4
|
+
|
5
|
+
from .mock import DictatureTableMock, DictatureBackendMock, Value, ValueMode
|
6
|
+
|
7
|
+
try:
|
8
|
+
from pymisp import PyMISP, MISPEvent, MISPAttribute
|
9
|
+
except ImportError as e:
|
10
|
+
raise ImportError("Please install the 'pymisp' package to use the 'DictatureBackendMISP' backend.") from e
|
11
|
+
|
12
|
+
|
13
|
+
class DictatureBackendMISP(DictatureBackendMock):
|
14
|
+
def __init__(self, misp: PyMISP, tag_name: str = 'storage:dictature', prefix: str = 'Dictature storage: ') -> None:
|
15
|
+
"""
|
16
|
+
Create a new MISP backend
|
17
|
+
:param misp: PyMISP instance
|
18
|
+
:param tag_name: tag name to use for the tables
|
19
|
+
:param prefix: prefix for the event names
|
20
|
+
"""
|
21
|
+
self.__misp = misp
|
22
|
+
self.__tag_name = tag_name
|
23
|
+
self.__prefix = prefix
|
24
|
+
|
25
|
+
def keys(self) -> Iterable[str]:
|
26
|
+
for event in self.__misp.search(tags=[self.__tag_name], pythonify=True):
|
27
|
+
name = event.info
|
28
|
+
if not name.startswith(self.__prefix):
|
29
|
+
continue
|
30
|
+
yield name[len(self.__prefix):]
|
31
|
+
|
32
|
+
def table(self, name: str) -> 'DictatureTableMock':
|
33
|
+
return DictatureTableMISP(self.__misp, self.__prefix + name, self.__tag_name)
|
34
|
+
|
35
|
+
|
36
|
+
class DictatureTableMISP(DictatureTableMock):
|
37
|
+
def __init__(self, misp: PyMISP, event_description: str, tag: str) -> None:
|
38
|
+
self.__misp = misp
|
39
|
+
self.__event_description = event_description
|
40
|
+
self.__tag = tag
|
41
|
+
self.__event: Optional[MISPEvent] = None
|
42
|
+
|
43
|
+
def keys(self) -> Iterable[str]:
|
44
|
+
for attribute in self.__event_attributes():
|
45
|
+
yield attribute.value
|
46
|
+
|
47
|
+
def drop(self) -> None:
|
48
|
+
self.__misp.delete_event(self.__get_event())
|
49
|
+
|
50
|
+
def create(self) -> None:
|
51
|
+
self.__get_event()
|
52
|
+
|
53
|
+
def set(self, item: str, value: Value) -> None:
|
54
|
+
save_as_json = value.mode != ValueMode.string.value or value.value.startswith('{')
|
55
|
+
save_data = dumps({'value': value.value, 'mode': value.mode}, indent=1) if save_as_json else value.value
|
56
|
+
|
57
|
+
for attribute in self.__event_attributes():
|
58
|
+
if attribute.value == item:
|
59
|
+
attribute.value = item
|
60
|
+
attribute.comment = save_data
|
61
|
+
self.__misp.update_attribute(attribute)
|
62
|
+
break
|
63
|
+
else:
|
64
|
+
attribute = MISPAttribute()
|
65
|
+
attribute.value = item
|
66
|
+
attribute.comment = save_data
|
67
|
+
attribute.type = 'comment'
|
68
|
+
attribute.to_ids = False
|
69
|
+
attribute.disable_correlation = True
|
70
|
+
self.__misp.add_attribute(self.__get_event(), attribute)
|
71
|
+
self.__get_event().attributes.append(attribute)
|
72
|
+
|
73
|
+
def get(self, item: str) -> Value:
|
74
|
+
for attribute in self.__event_attributes():
|
75
|
+
if attribute.value == item:
|
76
|
+
if attribute.comment.startswith('{'):
|
77
|
+
data = loads(attribute.comment)
|
78
|
+
return Value(data['value'], data['mode'])
|
79
|
+
return Value(attribute.comment, ValueMode.string.value)
|
80
|
+
raise KeyError(item)
|
81
|
+
|
82
|
+
def delete(self, item: str) -> None:
|
83
|
+
for attribute in self.__event_attributes():
|
84
|
+
if attribute.value == item:
|
85
|
+
# First update the attribute as deletion is not recognized immediately
|
86
|
+
attribute.type = 'other'
|
87
|
+
self.__misp.update_attribute(attribute)
|
88
|
+
self.__misp.delete_attribute(attribute)
|
89
|
+
break
|
90
|
+
|
91
|
+
def __get_event(self) -> MISPEvent:
|
92
|
+
if self.__event is None:
|
93
|
+
for event in self.__misp.search(tags=[self.__tag], eventinfo=self.__event_description, pythonify=True):
|
94
|
+
if event.info == self.__event_description:
|
95
|
+
self.__event = event
|
96
|
+
break
|
97
|
+
else:
|
98
|
+
event = MISPEvent()
|
99
|
+
event.info = self.__event_description
|
100
|
+
event.distribution = 0
|
101
|
+
event.threat_level_id = 4
|
102
|
+
event.analysis = 0
|
103
|
+
event.add_tag(self.__tag)
|
104
|
+
self.__misp.add_event(event)
|
105
|
+
self.__event = event
|
106
|
+
return self.__event
|
107
|
+
|
108
|
+
def __event_attributes(self) -> Iterable[MISPAttribute]:
|
109
|
+
for attribute in self.__get_event().attributes:
|
110
|
+
if attribute.type != 'comment' or (hasattr(attribute, 'deleted') and attribute.deleted):
|
111
|
+
continue
|
112
|
+
yield attribute
|
113
|
+
|
114
|
+
@staticmethod
|
115
|
+
def _record_encode(name: str, suffix: str = '.txt') -> str:
|
116
|
+
if name == sub(r'[^\w_. -]', '_', name):
|
117
|
+
return f"d_{name}{suffix}"
|
118
|
+
name = name.encode('utf-8').hex()
|
119
|
+
return f'e_{name}{suffix}'
|
120
|
+
|
121
|
+
@staticmethod
|
122
|
+
def _record_decode(name: str, suffix: str = '.txt') -> str:
|
123
|
+
encoded_name = name[2:-len(suffix) if suffix else len(name)]
|
124
|
+
if name.startswith('d_'):
|
125
|
+
return encoded_name
|
126
|
+
return bytes.fromhex(encoded_name).decode('utf-8')
|
dictature/backend/mock.py
CHANGED
@@ -15,27 +15,65 @@ class Value(NamedTuple):
|
|
15
15
|
|
16
16
|
class DictatureBackendMock:
|
17
17
|
def keys(self) -> Iterable[str]:
|
18
|
+
"""
|
19
|
+
Return all table names
|
20
|
+
:return: all table names
|
21
|
+
"""
|
18
22
|
raise NotImplementedError("This method should be implemented by the subclass")
|
19
23
|
|
20
24
|
def table(self, name: str) -> 'DictatureTableMock':
|
25
|
+
"""
|
26
|
+
Create a table object based on the name
|
27
|
+
:param name: name of the table
|
28
|
+
:return: table object
|
29
|
+
"""
|
21
30
|
raise NotImplementedError("This method should be implemented by the subclass")
|
22
31
|
|
23
32
|
|
24
33
|
class DictatureTableMock:
|
25
34
|
def keys(self) -> Iterable[str]:
|
35
|
+
"""
|
36
|
+
Return all keys in the table
|
37
|
+
:return: all keys in the table
|
38
|
+
"""
|
26
39
|
raise NotImplementedError("This method should be implemented by the subclass")
|
27
40
|
|
28
41
|
def drop(self) -> None:
|
42
|
+
"""
|
43
|
+
Delete the table
|
44
|
+
:return: None
|
45
|
+
"""
|
29
46
|
raise NotImplementedError("This method should be implemented by the subclass")
|
30
47
|
|
31
48
|
def create(self) -> None:
|
49
|
+
"""
|
50
|
+
Create the table in the backend
|
51
|
+
:return: None
|
52
|
+
"""
|
32
53
|
raise NotImplementedError("This method should be implemented by the subclass")
|
33
54
|
|
34
55
|
def set(self, item: str, value: Value) -> None:
|
56
|
+
"""
|
57
|
+
Set a value in the table
|
58
|
+
:param item: key to set
|
59
|
+
:param value: value to set
|
60
|
+
:return: None
|
61
|
+
"""
|
35
62
|
raise NotImplementedError("This method should be implemented by the subclass")
|
36
63
|
|
37
64
|
def get(self, item: str) -> Value:
|
65
|
+
"""
|
66
|
+
Get a value from the table
|
67
|
+
:param item: key to get
|
68
|
+
:return: value
|
69
|
+
:raises KeyError: if the key does not exist
|
70
|
+
"""
|
38
71
|
raise NotImplementedError("This method should be implemented by the subclass")
|
39
72
|
|
40
73
|
def delete(self, item: str) -> None:
|
74
|
+
"""
|
75
|
+
Delete a value from the table
|
76
|
+
:param item: key to delete
|
77
|
+
:return: None
|
78
|
+
"""
|
41
79
|
raise NotImplementedError("This method should be implemented by the subclass")
|
dictature/backend/sqlite.py
CHANGED
@@ -7,6 +7,10 @@ from .mock import DictatureBackendMock, DictatureTableMock, Value, ValueMode
|
|
7
7
|
|
8
8
|
class DictatureBackendSQLite(DictatureBackendMock):
|
9
9
|
def __init__(self, file: Union[str, Path]) -> None:
|
10
|
+
"""
|
11
|
+
Create a new SQLite backend
|
12
|
+
:param file: file to store the database
|
13
|
+
"""
|
10
14
|
if isinstance(file, str):
|
11
15
|
file = Path(file)
|
12
16
|
self.__file = file
|
@@ -33,7 +37,6 @@ class DictatureBackendSQLite(DictatureBackendMock):
|
|
33
37
|
self.__connection.close()
|
34
38
|
|
35
39
|
|
36
|
-
|
37
40
|
class DictatureTableSQLite(DictatureTableMock):
|
38
41
|
def __init__(self, parent: "DictatureBackendSQLite", name: str) -> None:
|
39
42
|
self.__parent = parent
|
dictature/dictature.py
CHANGED
@@ -17,30 +17,63 @@ class Dictature:
|
|
17
17
|
value_transformer: MockTransformer = PassthroughTransformer(),
|
18
18
|
table_name_transformer: Optional[MockTransformer] = None,
|
19
19
|
) -> None:
|
20
|
+
"""
|
21
|
+
Create a new Dictature object
|
22
|
+
:param backend: backend to use
|
23
|
+
:param name_transformer: transformer to use for table and key names
|
24
|
+
:param value_transformer: transformer to use for values
|
25
|
+
:param table_name_transformer: transformer to use for table names, if None, name_transformer is used
|
26
|
+
"""
|
20
27
|
self.__backend = backend
|
21
28
|
self.__table_cache: Dict[str, "DictatureTable"] = {}
|
22
29
|
self.__name_transformer = name_transformer
|
23
30
|
self.__value_transformer = value_transformer
|
24
31
|
self.__table_name_transformer = table_name_transformer or name_transformer
|
32
|
+
self.__cache_size = 4096
|
25
33
|
|
26
34
|
def keys(self) -> Set[str]:
|
35
|
+
"""
|
36
|
+
Return all table names
|
37
|
+
:return: all table names
|
38
|
+
"""
|
27
39
|
return set(map(self.__name_transformer.backward, self.__backend.keys()))
|
28
40
|
|
29
41
|
def values(self) -> Iterator["DictatureTable"]:
|
42
|
+
"""
|
43
|
+
Return all tables
|
44
|
+
:return: all tables
|
45
|
+
"""
|
30
46
|
return map(lambda x: x[1], self.items())
|
31
47
|
|
32
48
|
def items(self) -> Iterator[Tuple[str, "DictatureTable"]]:
|
49
|
+
"""
|
50
|
+
Return all tables with their instances
|
51
|
+
:return: all tables with their instances
|
52
|
+
"""
|
33
53
|
for k in self.keys():
|
34
54
|
yield k, self[k]
|
35
55
|
|
36
56
|
def to_dict(self) -> Dict[str, Any]:
|
57
|
+
"""
|
58
|
+
Return all tables as a dictionary
|
59
|
+
:return: all tables as a dictionary
|
60
|
+
"""
|
37
61
|
return {k: v.to_dict() for k, v in self.items()}
|
38
62
|
|
39
63
|
def __str__(self):
|
64
|
+
"""
|
65
|
+
Return all tables as a string
|
66
|
+
:return: all tables as a string
|
67
|
+
"""
|
40
68
|
return str(self.to_dict())
|
41
69
|
|
42
70
|
def __getitem__(self, item: str) -> "DictatureTable":
|
43
|
-
|
71
|
+
"""
|
72
|
+
Get a table by name
|
73
|
+
:param item: name of the table
|
74
|
+
:return: table instance
|
75
|
+
"""
|
76
|
+
if len(self.__table_cache) > self.__cache_size:
|
44
77
|
del self.__table_cache[choice(list(self.__table_cache.keys()))]
|
45
78
|
if item not in self.__table_cache:
|
46
79
|
self.__table_cache[item] = DictatureTable(
|
@@ -52,12 +85,26 @@ class Dictature:
|
|
52
85
|
return self.__table_cache[item]
|
53
86
|
|
54
87
|
def __delitem__(self, key: str) -> None:
|
88
|
+
"""
|
89
|
+
Delete a table
|
90
|
+
:param key: name of the table
|
91
|
+
:return: None
|
92
|
+
"""
|
55
93
|
self[key].drop()
|
56
94
|
|
57
95
|
def __contains__(self, item: str) -> bool:
|
96
|
+
"""
|
97
|
+
Check if a table exists
|
98
|
+
:param item: name of the table
|
99
|
+
:return: True if the table exists, False otherwise
|
100
|
+
"""
|
58
101
|
return item in self.keys()
|
59
102
|
|
60
103
|
def __bool__(self) -> bool:
|
104
|
+
"""
|
105
|
+
Check if there are any tables
|
106
|
+
:return: True if there are tables, False otherwise
|
107
|
+
"""
|
61
108
|
return not not self.keys()
|
62
109
|
|
63
110
|
|
@@ -69,6 +116,13 @@ class DictatureTable:
|
|
69
116
|
name_transformer: MockTransformer = PassthroughTransformer(),
|
70
117
|
value_transformer: MockTransformer = PassthroughTransformer()
|
71
118
|
):
|
119
|
+
"""
|
120
|
+
Create a new DictatureTable object
|
121
|
+
:param backend: backend to use
|
122
|
+
:param table_name: name of the table
|
123
|
+
:param name_transformer: transformer to use for key names
|
124
|
+
:param value_transformer: transformer to use for values
|
125
|
+
"""
|
72
126
|
self.__backend = backend
|
73
127
|
self.__name_transformer = name_transformer
|
74
128
|
self.__value_transformer = value_transformer
|
@@ -76,37 +130,77 @@ class DictatureTable:
|
|
76
130
|
self.__table_created = False
|
77
131
|
|
78
132
|
def get(self, item: str, default: Optional[Any] = None) -> Any:
|
133
|
+
"""
|
134
|
+
Get a value from the table
|
135
|
+
:param item: key to get
|
136
|
+
:param default: default value to return if the key does not exist
|
137
|
+
:return: value or default
|
138
|
+
"""
|
79
139
|
try:
|
80
140
|
return self[item]
|
81
141
|
except KeyError:
|
82
142
|
return default
|
83
143
|
|
84
144
|
def key_exists(self, item: str) -> bool:
|
145
|
+
"""
|
146
|
+
Check if a key exists
|
147
|
+
:param item: key to check
|
148
|
+
:return: True if the key exists, False otherwise
|
149
|
+
"""
|
85
150
|
self.__create_table()
|
86
151
|
return item in self.keys()
|
87
152
|
|
88
153
|
def keys(self) -> Set[str]:
|
154
|
+
"""
|
155
|
+
Return all keys in the table
|
156
|
+
:return: all keys in the table
|
157
|
+
"""
|
89
158
|
self.__create_table()
|
90
159
|
return set(map(self.__name_transformer.backward, self.__table.keys()))
|
91
160
|
|
92
161
|
def values(self) -> Iterator[Any]:
|
162
|
+
"""
|
163
|
+
Return all values in the table
|
164
|
+
:return: all values in the table
|
165
|
+
"""
|
93
166
|
return map(lambda x: x[1], self.items())
|
94
167
|
|
95
168
|
def items(self) -> Iterator[Tuple[str, Any]]:
|
169
|
+
"""
|
170
|
+
Return all items in the table
|
171
|
+
:return: all items in the table
|
172
|
+
"""
|
96
173
|
for k in self.keys():
|
97
174
|
yield k, self[k]
|
98
175
|
|
99
176
|
def drop(self) -> None:
|
177
|
+
"""
|
178
|
+
Delete the table
|
179
|
+
:return: None
|
180
|
+
"""
|
100
181
|
self.__create_table()
|
101
182
|
self.__table.drop()
|
102
183
|
|
103
184
|
def to_dict(self) -> Dict[str, Any]:
|
185
|
+
"""
|
186
|
+
Return all items as a dictionary
|
187
|
+
:return: all items as a dictionary
|
188
|
+
"""
|
104
189
|
return {k: v for k, v in self.items()}
|
105
190
|
|
106
191
|
def __str__(self):
|
192
|
+
"""
|
193
|
+
Return all items as a string
|
194
|
+
:return: all items as a string
|
195
|
+
"""
|
107
196
|
return str(self.to_dict())
|
108
197
|
|
109
198
|
def __getitem__(self, item: str) -> Any:
|
199
|
+
"""
|
200
|
+
Get a value from the table
|
201
|
+
:param item: key to get
|
202
|
+
:return: value
|
203
|
+
"""
|
110
204
|
self.__create_table()
|
111
205
|
saved_value = self.__table.get(self.__item_key(item))
|
112
206
|
mode = ValueMode(saved_value.mode)
|
@@ -120,6 +214,12 @@ class DictatureTable:
|
|
120
214
|
raise ValueError(f"Unknown mode '{mode}'")
|
121
215
|
|
122
216
|
def __setitem__(self, key: str, value: Any) -> None:
|
217
|
+
"""
|
218
|
+
Set a value in the table
|
219
|
+
:param key: key to set
|
220
|
+
:param value: value to set
|
221
|
+
:return: None
|
222
|
+
"""
|
123
223
|
self.__create_table()
|
124
224
|
value_mode = ValueMode.string
|
125
225
|
|
@@ -136,21 +236,44 @@ class DictatureTable:
|
|
136
236
|
self.__table.set(key, Value(value=value, mode=value_mode.value))
|
137
237
|
|
138
238
|
def __delitem__(self, key: str) -> None:
|
239
|
+
"""
|
240
|
+
Delete a key from the table
|
241
|
+
:param key: key to delete
|
242
|
+
:return: None
|
243
|
+
"""
|
139
244
|
self.__table.delete(self.__item_key(key))
|
140
245
|
|
141
246
|
def __contains__(self, item: str):
|
247
|
+
"""
|
248
|
+
Check if a key exists
|
249
|
+
:param item: key to check
|
250
|
+
:return: True if the key exists, False otherwise
|
251
|
+
"""
|
142
252
|
return item in self.keys()
|
143
253
|
|
144
254
|
def __bool__(self) -> bool:
|
255
|
+
"""
|
256
|
+
Check if there are any items in the table
|
257
|
+
:return: True if there are items, False otherwise
|
258
|
+
"""
|
145
259
|
return not not self.keys()
|
146
260
|
|
147
261
|
def __create_table(self) -> None:
|
262
|
+
"""
|
263
|
+
Create the table if it does not exist
|
264
|
+
:return: None
|
265
|
+
"""
|
148
266
|
if self.__table_created:
|
149
267
|
return
|
150
268
|
self.__table.create()
|
151
269
|
self.__table_created = True
|
152
270
|
|
153
271
|
def __item_key(self, item: str) -> str:
|
272
|
+
"""
|
273
|
+
Transform the key for storage
|
274
|
+
:param item: key to transform
|
275
|
+
:return: transformed key
|
276
|
+
"""
|
154
277
|
if not self.__name_transformer.static:
|
155
278
|
for key in self.__table.keys():
|
156
279
|
if self.__name_transformer.backward(key) == item:
|
@@ -158,6 +281,11 @@ class DictatureTable:
|
|
158
281
|
return self.__name_transformer.forward(item)
|
159
282
|
|
160
283
|
def __table_key(self, table_name: str) -> str:
|
284
|
+
"""
|
285
|
+
Transform the table name for storage
|
286
|
+
:param table_name: table name to transform
|
287
|
+
:return: transformed table name
|
288
|
+
"""
|
161
289
|
if not self.__name_transformer.static:
|
162
290
|
for key in self.__backend.keys():
|
163
291
|
if self.__name_transformer.backward(key) == table_name:
|
dictature/transformer/aes.py
CHANGED
@@ -10,6 +10,12 @@ from .mock import MockTransformer
|
|
10
10
|
|
11
11
|
class AESTransformer(MockTransformer):
|
12
12
|
def __init__(self, passphrase: str, static_names_mode: bool, salt: str = 'dictature') -> None:
|
13
|
+
"""
|
14
|
+
Create a new AES transformer
|
15
|
+
:param passphrase: secret passphrase to encrypt/decrypt the data
|
16
|
+
:param static_names_mode: if True, the transformer will use ECB mode instead of GCM (True decreases security, increases speed)
|
17
|
+
:param salt: salt to use for the key derivation
|
18
|
+
"""
|
13
19
|
self.__key = scrypt(passphrase, salt, 16, N=2 ** 14, r=8, p=1)
|
14
20
|
self.__mode = AES.MODE_GCM if not static_names_mode else AES.MODE_ECB
|
15
21
|
self.__static = static_names_mode
|
dictature/transformer/hmac.py
CHANGED
@@ -5,6 +5,10 @@ from .mock import MockTransformer
|
|
5
5
|
|
6
6
|
class HmacTransformer(MockTransformer):
|
7
7
|
def __init__(self, secret: str = 'dictature') -> None:
|
8
|
+
"""
|
9
|
+
Perform HMAC on the text.
|
10
|
+
:param secret: secret key to use for HMAC, if not provided works as a simple hash function
|
11
|
+
"""
|
8
12
|
self.__secret = secret
|
9
13
|
|
10
14
|
def forward(self, text: str) -> str:
|
dictature/transformer/mock.py
CHANGED
@@ -1,9 +1,25 @@
|
|
1
1
|
|
2
2
|
class MockTransformer:
|
3
3
|
def forward(self, text: str) -> str:
|
4
|
+
"""
|
5
|
+
Transform the text in some way to the data format in data storage
|
6
|
+
:param text: text to transform
|
7
|
+
:return: transformed text
|
8
|
+
"""
|
4
9
|
raise NotImplementedError("This method should be implemented by the child class")
|
10
|
+
|
5
11
|
def backward(self, text: str) -> str:
|
12
|
+
"""
|
13
|
+
Transform the data format in data storage to the text
|
14
|
+
:param text: text to transform
|
15
|
+
:return: original text
|
16
|
+
"""
|
6
17
|
raise NotImplementedError("This method should be implemented by the child class")
|
18
|
+
|
7
19
|
@property
|
8
20
|
def static(self) -> bool:
|
21
|
+
"""
|
22
|
+
Returns True only if when the forward transformation is applied to the same text, the result is always the same
|
23
|
+
:return: True if the transformation is static
|
24
|
+
"""
|
9
25
|
raise NotImplementedError("This method should be implemented by the child class")
|
@@ -1,10 +1,17 @@
|
|
1
1
|
from .mock import MockTransformer
|
2
2
|
|
3
|
+
|
3
4
|
class PassthroughTransformer(MockTransformer):
|
5
|
+
"""
|
6
|
+
Passthrough transformer, does not modify the text.
|
7
|
+
"""
|
8
|
+
|
4
9
|
def forward(self, text: str) -> str:
|
5
10
|
return text
|
11
|
+
|
6
12
|
def backward(self, text: str) -> str:
|
7
13
|
return text
|
14
|
+
|
8
15
|
@property
|
9
16
|
def static(self) -> bool:
|
10
17
|
return True
|
@@ -5,6 +5,10 @@ from .mock import MockTransformer
|
|
5
5
|
|
6
6
|
class PipelineTransformer(MockTransformer):
|
7
7
|
def __init__(self, transformers: List[MockTransformer]) -> None:
|
8
|
+
"""
|
9
|
+
Create a pipeline of transformers. The text is passed through each transformer in the order they are provided.
|
10
|
+
:param transformers: list of transformers to use
|
11
|
+
"""
|
8
12
|
self.__transformers = transformers
|
9
13
|
|
10
14
|
def forward(self, text: str) -> str:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dictature
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.4
|
4
4
|
Summary: dictature -- A generic wrapper around dict-like interface with mulitple backends
|
5
5
|
Author-email: Adam Hlavacek <git@adamhlavacek.com>
|
6
6
|
Project-URL: Homepage, https://github.com/esoadamo/dictature
|
@@ -53,6 +53,7 @@ del dictionary['test'] # drops whole table
|
|
53
53
|
Currently, the following backends are supported:
|
54
54
|
- `DictatureBackendDirectory`: stores the data in a directory as json files
|
55
55
|
- `DictatureBackendSQLite`: stores the data in a SQLite database
|
56
|
+
- `DictatureBackendMISP`: stores the data in a MISP instance
|
56
57
|
|
57
58
|
### Transformers
|
58
59
|
|
@@ -73,3 +74,9 @@ dictionary = Dictature(
|
|
73
74
|
value_transformer=value_transformer
|
74
75
|
)
|
75
76
|
```
|
77
|
+
|
78
|
+
Currently, the following transformers are supported:
|
79
|
+
- `AESTransformer`: encrypts/decrypts the data using AES
|
80
|
+
- `HmacTransformer`: signs the data using HMAC or performs hash integrity checks
|
81
|
+
- `PassthroughTransformer`: does nothing
|
82
|
+
- `PipelineTransformer`: chains multiple transformers
|
@@ -0,0 +1,18 @@
|
|
1
|
+
dictature/__init__.py,sha256=UCPJKHeyirRZ0pCYoyeat-rwXa8pDezOJ3UWCipDdyc,33
|
2
|
+
dictature/dictature.py,sha256=VM4RcXftQl2cFY8GxsRl7h3K8tsH7urNoT7GyGsxGuo,9314
|
3
|
+
dictature/backend/__init__.py,sha256=d5s6QCJOUzFglVNg8Cqqx_8b61S-AOTGjEUIF6FS69U,149
|
4
|
+
dictature/backend/directory.py,sha256=Wf6dtllkqVA70DsnFoFzEHi_-RrTeOwqX5TFU-mYjrk,3451
|
5
|
+
dictature/backend/misp.py,sha256=ZT392TkAkoP1fB5ZXjArD1Vsm00hvvq08msCv6QXbh8,4971
|
6
|
+
dictature/backend/mock.py,sha256=Bllai6uZ1cRL8mKLbB3YQQo26mxTFepqzGu3FfOL1d4,2183
|
7
|
+
dictature/backend/sqlite.py,sha256=zyphYEeLY4eGuBCor16i80_-brdipMpXZ3_kONwErsE,5237
|
8
|
+
dictature/transformer/__init__.py,sha256=JIFJpXU6iB9hIUM8L7HL2o9Nqjm_YbMEuQBQC8ZJ6b4,124
|
9
|
+
dictature/transformer/aes.py,sha256=ZhC1dT9QpnziErkDLriWLgXDEFNGQW0KG4aqSN2AZpA,1926
|
10
|
+
dictature/transformer/hmac.py,sha256=vURsB0HlzRPn_Vkl7lGmZV9OKempQuds8AanmadDxIo,834
|
11
|
+
dictature/transformer/mock.py,sha256=7zu65ZqUV_AVRaPSzNd73cVMXixXt31SeuX9OKZxaJQ,948
|
12
|
+
dictature/transformer/passthrough.py,sha256=Pt3N6G_Qh6HJ_q75ETL5nfAwYHLB-SjkVwUwbbbMik8,344
|
13
|
+
dictature/transformer/pipeline.py,sha256=OaQaJeJ5NpICetJe08r8ontqstsXGuW8jDbKw1zxYs4,842
|
14
|
+
dictature-0.9.4.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
|
15
|
+
dictature-0.9.4.dist-info/METADATA,sha256=QklI5x9PnsuJZ-6jIcFskCDxSVveI3le5IJSIh_8E4Q,2826
|
16
|
+
dictature-0.9.4.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
17
|
+
dictature-0.9.4.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
|
18
|
+
dictature-0.9.4.dist-info/RECORD,,
|
dictature-0.9.3.dist-info/RECORD
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
dictature/__init__.py,sha256=UCPJKHeyirRZ0pCYoyeat-rwXa8pDezOJ3UWCipDdyc,33
|
2
|
-
dictature/dictature.py,sha256=eFjUc5Q1DUXLNqk-UeHzFqWgCZppTsoK3TXCJ1UuCS8,5666
|
3
|
-
dictature/backend/__init__.py,sha256=d5s6QCJOUzFglVNg8Cqqx_8b61S-AOTGjEUIF6FS69U,149
|
4
|
-
dictature/backend/directory.py,sha256=KVbKS1CibXmY1NsZRWuTE0uC4DXRLOXqYEHxyHJidCc,3266
|
5
|
-
dictature/backend/mock.py,sha256=Qd7KSh-qM763Jc7biDf5xYFWdgDax30dUHh2gXWwTZE,1266
|
6
|
-
dictature/backend/sqlite.py,sha256=aExNxDtx1kiPrZn-jfCzbpV4alEXyGc6f12tuCJK1tk,5130
|
7
|
-
dictature/transformer/__init__.py,sha256=JIFJpXU6iB9hIUM8L7HL2o9Nqjm_YbMEuQBQC8ZJ6b4,124
|
8
|
-
dictature/transformer/aes.py,sha256=6H3jNkUpgWBX88BduMzbi9MDSRxMHnWmZZEIJ70BLi0,1601
|
9
|
-
dictature/transformer/hmac.py,sha256=pYw6ybUIMoNdU2JFI9ffePr-33ails-CN9J6rFt7RVE,677
|
10
|
-
dictature/transformer/mock.py,sha256=osETvYZjlgos0trJy0YvXcmtNy0L6x2h2099t1aHMFc,421
|
11
|
-
dictature/transformer/passthrough.py,sha256=63hZCPQMUJa-G6ZKdv_xt2fMiMZpmPoL84PY5eb2ueE,269
|
12
|
-
dictature/transformer/pipeline.py,sha256=-2r9FxLXEnk3qpCfXC0qp0KqNC2qkpChCJYEbZAQRYM,642
|
13
|
-
dictature-0.9.3.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
|
14
|
-
dictature-0.9.3.dist-info/METADATA,sha256=BPA99McqtwhQxjALGLwrWvaZiqRx3XHnjYZ5tEfxYIk,2478
|
15
|
-
dictature-0.9.3.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
16
|
-
dictature-0.9.3.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
|
17
|
-
dictature-0.9.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|