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.
@@ -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")
@@ -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
- 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:
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:
@@ -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
@@ -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:
@@ -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
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,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,,