dictature 0.9.2__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.
@@ -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
@@ -71,7 +76,8 @@ class DictatureTableDirectory(DictatureTableMock):
71
76
  def _filename_encode(name: str, suffix: str = '.txt') -> str:
72
77
  if name == sub(r'[^\w_. -]', '_', name):
73
78
  return f"d_{name}{suffix}"
74
- return f'e_{name.encode('utf-8').hex()}{suffix}'
79
+ name = name.encode('utf-8').hex()
80
+ return f'e_{name}{suffix}'
75
81
 
76
82
  @staticmethod
77
83
  def _filename_decode(name: str, suffix: str = '.txt') -> str:
@@ -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")
@@ -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
@@ -15,30 +15,65 @@ class Dictature:
15
15
  backend: DictatureBackendMock,
16
16
  name_transformer: MockTransformer = PassthroughTransformer(),
17
17
  value_transformer: MockTransformer = PassthroughTransformer(),
18
+ table_name_transformer: Optional[MockTransformer] = None,
18
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
+ """
19
27
  self.__backend = backend
20
28
  self.__table_cache: Dict[str, "DictatureTable"] = {}
21
29
  self.__name_transformer = name_transformer
22
30
  self.__value_transformer = value_transformer
31
+ self.__table_name_transformer = table_name_transformer or name_transformer
32
+ self.__cache_size = 4096
23
33
 
24
34
  def keys(self) -> Set[str]:
35
+ """
36
+ Return all table names
37
+ :return: all table names
38
+ """
25
39
  return set(map(self.__name_transformer.backward, self.__backend.keys()))
26
40
 
27
41
  def values(self) -> Iterator["DictatureTable"]:
42
+ """
43
+ Return all tables
44
+ :return: all tables
45
+ """
28
46
  return map(lambda x: x[1], self.items())
29
47
 
30
48
  def items(self) -> Iterator[Tuple[str, "DictatureTable"]]:
49
+ """
50
+ Return all tables with their instances
51
+ :return: all tables with their instances
52
+ """
31
53
  for k in self.keys():
32
54
  yield k, self[k]
33
55
 
34
56
  def to_dict(self) -> Dict[str, Any]:
57
+ """
58
+ Return all tables as a dictionary
59
+ :return: all tables as a dictionary
60
+ """
35
61
  return {k: v.to_dict() for k, v in self.items()}
36
62
 
37
63
  def __str__(self):
64
+ """
65
+ Return all tables as a string
66
+ :return: all tables as a string
67
+ """
38
68
  return str(self.to_dict())
39
69
 
40
70
  def __getitem__(self, item: str) -> "DictatureTable":
41
- if len(self.__table_cache) > 128:
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:
42
77
  del self.__table_cache[choice(list(self.__table_cache.keys()))]
43
78
  if item not in self.__table_cache:
44
79
  self.__table_cache[item] = DictatureTable(
@@ -50,12 +85,26 @@ class Dictature:
50
85
  return self.__table_cache[item]
51
86
 
52
87
  def __delitem__(self, key: str) -> None:
88
+ """
89
+ Delete a table
90
+ :param key: name of the table
91
+ :return: None
92
+ """
53
93
  self[key].drop()
54
94
 
55
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
+ """
56
101
  return item in self.keys()
57
102
 
58
103
  def __bool__(self) -> bool:
104
+ """
105
+ Check if there are any tables
106
+ :return: True if there are tables, False otherwise
107
+ """
59
108
  return not not self.keys()
60
109
 
61
110
 
@@ -67,6 +116,13 @@ class DictatureTable:
67
116
  name_transformer: MockTransformer = PassthroughTransformer(),
68
117
  value_transformer: MockTransformer = PassthroughTransformer()
69
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
+ """
70
126
  self.__backend = backend
71
127
  self.__name_transformer = name_transformer
72
128
  self.__value_transformer = value_transformer
@@ -74,51 +130,96 @@ class DictatureTable:
74
130
  self.__table_created = False
75
131
 
76
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
+ """
77
139
  try:
78
140
  return self[item]
79
141
  except KeyError:
80
142
  return default
81
143
 
82
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
+ """
83
150
  self.__create_table()
84
151
  return item in self.keys()
85
152
 
86
153
  def keys(self) -> Set[str]:
154
+ """
155
+ Return all keys in the table
156
+ :return: all keys in the table
157
+ """
87
158
  self.__create_table()
88
159
  return set(map(self.__name_transformer.backward, self.__table.keys()))
89
160
 
90
161
  def values(self) -> Iterator[Any]:
162
+ """
163
+ Return all values in the table
164
+ :return: all values in the table
165
+ """
91
166
  return map(lambda x: x[1], self.items())
92
167
 
93
168
  def items(self) -> Iterator[Tuple[str, Any]]:
169
+ """
170
+ Return all items in the table
171
+ :return: all items in the table
172
+ """
94
173
  for k in self.keys():
95
174
  yield k, self[k]
96
175
 
97
176
  def drop(self) -> None:
177
+ """
178
+ Delete the table
179
+ :return: None
180
+ """
98
181
  self.__create_table()
99
182
  self.__table.drop()
100
183
 
101
184
  def to_dict(self) -> Dict[str, Any]:
185
+ """
186
+ Return all items as a dictionary
187
+ :return: all items as a dictionary
188
+ """
102
189
  return {k: v for k, v in self.items()}
103
190
 
104
191
  def __str__(self):
192
+ """
193
+ Return all items as a string
194
+ :return: all items as a string
195
+ """
105
196
  return str(self.to_dict())
106
197
 
107
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
+ """
108
204
  self.__create_table()
109
205
  saved_value = self.__table.get(self.__item_key(item))
110
206
  mode = ValueMode(saved_value.mode)
111
207
  value = self.__value_transformer.backward(saved_value.value)
112
- match mode:
113
- case ValueMode.string:
114
- return value
115
- case ValueMode.json:
116
- return json.loads(value)
117
- case ValueMode.pickle:
118
- return pickle.loads(decompress(b64decode(value.encode('ascii'))))
208
+ if mode == ValueMode.string:
209
+ return value
210
+ elif mode == ValueMode.json:
211
+ return json.loads(value)
212
+ elif mode == ValueMode.pickle:
213
+ return pickle.loads(decompress(b64decode(value.encode('ascii'))))
119
214
  raise ValueError(f"Unknown mode '{mode}'")
120
215
 
121
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
+ """
122
223
  self.__create_table()
123
224
  value_mode = ValueMode.string
124
225
 
@@ -135,21 +236,44 @@ class DictatureTable:
135
236
  self.__table.set(key, Value(value=value, mode=value_mode.value))
136
237
 
137
238
  def __delitem__(self, key: str) -> None:
138
- self.__table.delete(self.__name_transformer.forward(key))
239
+ """
240
+ Delete a key from the table
241
+ :param key: key to delete
242
+ :return: None
243
+ """
244
+ self.__table.delete(self.__item_key(key))
139
245
 
140
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
+ """
141
252
  return item in self.keys()
142
253
 
143
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
+ """
144
259
  return not not self.keys()
145
260
 
146
261
  def __create_table(self) -> None:
262
+ """
263
+ Create the table if it does not exist
264
+ :return: None
265
+ """
147
266
  if self.__table_created:
148
267
  return
149
268
  self.__table.create()
150
269
  self.__table_created = True
151
270
 
152
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
+ """
153
277
  if not self.__name_transformer.static:
154
278
  for key in self.__table.keys():
155
279
  if self.__name_transformer.backward(key) == item:
@@ -157,6 +281,11 @@ class DictatureTable:
157
281
  return self.__name_transformer.forward(item)
158
282
 
159
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
+ """
160
289
  if not self.__name_transformer.static:
161
290
  for key in self.__backend.keys():
162
291
  if self.__name_transformer.backward(key) == table_name:
@@ -1,2 +1,3 @@
1
1
  from .mock import MockTransformer
2
2
  from .passthrough import PassthroughTransformer
3
+ from .pipeline import PipelineTransformer
@@ -10,12 +10,18 @@ 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
16
22
 
17
23
  def forward(self, text: str) -> str:
18
- cipher = self.__cipher
24
+ cipher = self.__cipher()
19
25
  if self.__mode == AES.MODE_GCM:
20
26
  ciphertext, tag = cipher.encrypt_and_digest(pad(text.encode('utf8'), AES.block_size))
21
27
  return (cipher.nonce + tag + ciphertext).hex()
@@ -24,19 +30,15 @@ class AESTransformer(MockTransformer):
24
30
 
25
31
  def backward(self, text: str) -> str:
26
32
  data = bytes.fromhex(text)
27
- cipher = self.__cipher
28
33
  if self.__mode == AES.MODE_GCM:
29
34
  nonce, tag, ciphertext = data[:16], data[16:32], data[32:]
30
- # noinspection PyTypeChecker
31
- cipher = AES.new(self.__key, self.__mode, nonce=nonce)
32
- return unpad(cipher.decrypt_and_verify(ciphertext, tag), AES.block_size).decode('utf8')
35
+ return unpad(self.__cipher(nonce=nonce).decrypt_and_verify(ciphertext, tag), AES.block_size).decode('utf8')
33
36
  else:
34
- return unpad(cipher.decrypt(data), AES.block_size).decode('utf8')
37
+ return unpad(self.__cipher().decrypt(data), AES.block_size).decode('utf8')
35
38
 
36
- @property
37
- def __cipher(self) -> AES:
39
+ def __cipher(self, **kwargs) -> AES:
38
40
  # noinspection PyTypeChecker
39
- return AES.new(self.__key, self.__mode)
41
+ return AES.new(self.__key, self.__mode, **kwargs)
40
42
 
41
43
  @property
42
44
  def static(self) -> bool:
@@ -0,0 +1,28 @@
1
+ import hmac
2
+ from hashlib import sha256
3
+ from .mock import MockTransformer
4
+
5
+
6
+ class HmacTransformer(MockTransformer):
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
+ """
12
+ self.__secret = secret
13
+
14
+ def forward(self, text: str) -> str:
15
+ return f"{self.__hmac(text)}-{text}"
16
+
17
+ def backward(self, text: str) -> str:
18
+ mac, text = text.split('-', 1)
19
+ if mac != self.__hmac(text):
20
+ raise ValueError('Invalid HMAC')
21
+ return text
22
+
23
+ def __hmac(self, text: str) -> str:
24
+ return hmac.new(self.__secret.encode('utf8'), text.encode('utf8'), sha256).hexdigest()
25
+
26
+ @property
27
+ def static(self) -> bool:
28
+ return True
@@ -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
@@ -0,0 +1,26 @@
1
+ from typing import List
2
+
3
+ from .mock import MockTransformer
4
+
5
+
6
+ class PipelineTransformer(MockTransformer):
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
+ """
12
+ self.__transformers = transformers
13
+
14
+ def forward(self, text: str) -> str:
15
+ for transformer in self.__transformers:
16
+ text = transformer.forward(text)
17
+ return text
18
+
19
+ def backward(self, text: str) -> str:
20
+ for transformer in reversed(self.__transformers):
21
+ text = transformer.backward(text)
22
+ return text
23
+
24
+ @property
25
+ def static(self) -> bool:
26
+ return all(t.static for t in self.__transformers)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dictature
3
- Version: 0.9.2
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,,
@@ -1,15 +0,0 @@
1
- dictature/__init__.py,sha256=UCPJKHeyirRZ0pCYoyeat-rwXa8pDezOJ3UWCipDdyc,33
2
- dictature/dictature.py,sha256=nfoCcsI6pcXVZ6IoJLRFH7g42OJrUSVZFMf9jqCX0BQ,5551
3
- dictature/backend/__init__.py,sha256=d5s6QCJOUzFglVNg8Cqqx_8b61S-AOTGjEUIF6FS69U,149
4
- dictature/backend/directory.py,sha256=u_AIJnwjytTqwdjSz7DPp5aU3qX4kJyNp8dkzupSn_0,3246
5
- dictature/backend/mock.py,sha256=Qd7KSh-qM763Jc7biDf5xYFWdgDax30dUHh2gXWwTZE,1266
6
- dictature/backend/sqlite.py,sha256=aExNxDtx1kiPrZn-jfCzbpV4alEXyGc6f12tuCJK1tk,5130
7
- dictature/transformer/__init__.py,sha256=H3-ySHD-yZz9Zin2H9P619IdQiXh1e-yma669K4V_go,82
8
- dictature/transformer/aes.py,sha256=I1Nhr3pKIZ5K4fb4D1m1q4plrRU7Yllqid-ysv78KNk,1703
9
- dictature/transformer/mock.py,sha256=osETvYZjlgos0trJy0YvXcmtNy0L6x2h2099t1aHMFc,421
10
- dictature/transformer/passthrough.py,sha256=63hZCPQMUJa-G6ZKdv_xt2fMiMZpmPoL84PY5eb2ueE,269
11
- dictature-0.9.2.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
12
- dictature-0.9.2.dist-info/METADATA,sha256=ScBeTxvFHcr1EyGz5w2kFg6t-aXPBTDt0pkbfz-mhhY,2478
13
- dictature-0.9.2.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
14
- dictature-0.9.2.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
15
- dictature-0.9.2.dist-info/RECORD,,