dictature 0.9.1__py3-none-any.whl → 0.9.3__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 +2 -1
- dictature/dictature.py +26 -11
- dictature/transformer/__init__.py +1 -0
- dictature/transformer/aes.py +10 -9
- dictature/transformer/hmac.py +24 -0
- dictature/transformer/mock.py +3 -0
- dictature/transformer/passthrough.py +3 -0
- dictature/transformer/pipeline.py +22 -0
- {dictature-0.9.1.dist-info → dictature-0.9.3.dist-info}/METADATA +1 -1
- dictature-0.9.3.dist-info/RECORD +17 -0
- dictature-0.9.1.dist-info/RECORD +0 -15
- {dictature-0.9.1.dist-info → dictature-0.9.3.dist-info}/LICENSE +0 -0
- {dictature-0.9.1.dist-info → dictature-0.9.3.dist-info}/WHEEL +0 -0
- {dictature-0.9.1.dist-info → dictature-0.9.3.dist-info}/top_level.txt +0 -0
dictature/backend/directory.py
CHANGED
@@ -71,7 +71,8 @@ class DictatureTableDirectory(DictatureTableMock):
|
|
71
71
|
def _filename_encode(name: str, suffix: str = '.txt') -> str:
|
72
72
|
if name == sub(r'[^\w_. -]', '_', name):
|
73
73
|
return f"d_{name}{suffix}"
|
74
|
-
|
74
|
+
name = name.encode('utf-8').hex()
|
75
|
+
return f'e_{name}{suffix}'
|
75
76
|
|
76
77
|
@staticmethod
|
77
78
|
def _filename_decode(name: str, suffix: str = '.txt') -> str:
|
dictature/dictature.py
CHANGED
@@ -15,11 +15,13 @@ 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:
|
19
20
|
self.__backend = backend
|
20
21
|
self.__table_cache: Dict[str, "DictatureTable"] = {}
|
21
22
|
self.__name_transformer = name_transformer
|
22
23
|
self.__value_transformer = value_transformer
|
24
|
+
self.__table_name_transformer = table_name_transformer or name_transformer
|
23
25
|
|
24
26
|
def keys(self) -> Set[str]:
|
25
27
|
return set(map(self.__name_transformer.backward, self.__backend.keys()))
|
@@ -70,7 +72,7 @@ class DictatureTable:
|
|
70
72
|
self.__backend = backend
|
71
73
|
self.__name_transformer = name_transformer
|
72
74
|
self.__value_transformer = value_transformer
|
73
|
-
self.__table = self.__backend.table(self.
|
75
|
+
self.__table = self.__backend.table(self.__table_key(table_name))
|
74
76
|
self.__table_created = False
|
75
77
|
|
76
78
|
def get(self, item: str, default: Optional[Any] = None) -> Any:
|
@@ -106,16 +108,15 @@ class DictatureTable:
|
|
106
108
|
|
107
109
|
def __getitem__(self, item: str) -> Any:
|
108
110
|
self.__create_table()
|
109
|
-
saved_value = self.__table.get(self.
|
111
|
+
saved_value = self.__table.get(self.__item_key(item))
|
110
112
|
mode = ValueMode(saved_value.mode)
|
111
113
|
value = self.__value_transformer.backward(saved_value.value)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
return pickle.loads(decompress(b64decode(value.encode('ascii'))))
|
114
|
+
if mode == ValueMode.string:
|
115
|
+
return value
|
116
|
+
elif mode == ValueMode.json:
|
117
|
+
return json.loads(value)
|
118
|
+
elif mode == ValueMode.pickle:
|
119
|
+
return pickle.loads(decompress(b64decode(value.encode('ascii'))))
|
119
120
|
raise ValueError(f"Unknown mode '{mode}'")
|
120
121
|
|
121
122
|
def __setitem__(self, key: str, value: Any) -> None:
|
@@ -130,12 +131,12 @@ class DictatureTable:
|
|
130
131
|
value = b64encode(compress(pickle.dumps(value))).decode('ascii')
|
131
132
|
value_mode = value_mode.pickle
|
132
133
|
|
133
|
-
key = self.
|
134
|
+
key = self.__item_key(key)
|
134
135
|
value = self.__value_transformer.forward(value)
|
135
136
|
self.__table.set(key, Value(value=value, mode=value_mode.value))
|
136
137
|
|
137
138
|
def __delitem__(self, key: str) -> None:
|
138
|
-
self.__table.delete(self.
|
139
|
+
self.__table.delete(self.__item_key(key))
|
139
140
|
|
140
141
|
def __contains__(self, item: str):
|
141
142
|
return item in self.keys()
|
@@ -148,3 +149,17 @@ class DictatureTable:
|
|
148
149
|
return
|
149
150
|
self.__table.create()
|
150
151
|
self.__table_created = True
|
152
|
+
|
153
|
+
def __item_key(self, item: str) -> str:
|
154
|
+
if not self.__name_transformer.static:
|
155
|
+
for key in self.__table.keys():
|
156
|
+
if self.__name_transformer.backward(key) == item:
|
157
|
+
return key
|
158
|
+
return self.__name_transformer.forward(item)
|
159
|
+
|
160
|
+
def __table_key(self, table_name: str) -> str:
|
161
|
+
if not self.__name_transformer.static:
|
162
|
+
for key in self.__backend.keys():
|
163
|
+
if self.__name_transformer.backward(key) == table_name:
|
164
|
+
return key
|
165
|
+
return self.__name_transformer.forward(table_name)
|
dictature/transformer/aes.py
CHANGED
@@ -12,9 +12,10 @@ class AESTransformer(MockTransformer):
|
|
12
12
|
def __init__(self, passphrase: str, static_names_mode: bool, salt: str = 'dictature') -> None:
|
13
13
|
self.__key = scrypt(passphrase, salt, 16, N=2 ** 14, r=8, p=1)
|
14
14
|
self.__mode = AES.MODE_GCM if not static_names_mode else AES.MODE_ECB
|
15
|
+
self.__static = static_names_mode
|
15
16
|
|
16
17
|
def forward(self, text: str) -> str:
|
17
|
-
cipher = self.__cipher
|
18
|
+
cipher = self.__cipher()
|
18
19
|
if self.__mode == AES.MODE_GCM:
|
19
20
|
ciphertext, tag = cipher.encrypt_and_digest(pad(text.encode('utf8'), AES.block_size))
|
20
21
|
return (cipher.nonce + tag + ciphertext).hex()
|
@@ -23,16 +24,16 @@ class AESTransformer(MockTransformer):
|
|
23
24
|
|
24
25
|
def backward(self, text: str) -> str:
|
25
26
|
data = bytes.fromhex(text)
|
26
|
-
cipher = self.__cipher
|
27
27
|
if self.__mode == AES.MODE_GCM:
|
28
28
|
nonce, tag, ciphertext = data[:16], data[16:32], data[32:]
|
29
|
-
|
30
|
-
cipher = AES.new(self.__key, self.__mode, nonce=nonce)
|
31
|
-
return unpad(cipher.decrypt_and_verify(ciphertext, tag), AES.block_size).decode('utf8')
|
29
|
+
return unpad(self.__cipher(nonce=nonce).decrypt_and_verify(ciphertext, tag), AES.block_size).decode('utf8')
|
32
30
|
else:
|
33
|
-
return unpad(
|
31
|
+
return unpad(self.__cipher().decrypt(data), AES.block_size).decode('utf8')
|
34
32
|
|
35
|
-
|
36
|
-
def __cipher(self) -> AES:
|
33
|
+
def __cipher(self, **kwargs) -> AES:
|
37
34
|
# noinspection PyTypeChecker
|
38
|
-
return AES.new(self.__key, self.__mode)
|
35
|
+
return AES.new(self.__key, self.__mode, **kwargs)
|
36
|
+
|
37
|
+
@property
|
38
|
+
def static(self) -> bool:
|
39
|
+
return self.__static
|
@@ -0,0 +1,24 @@
|
|
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
|
+
self.__secret = secret
|
9
|
+
|
10
|
+
def forward(self, text: str) -> str:
|
11
|
+
return f"{self.__hmac(text)}-{text}"
|
12
|
+
|
13
|
+
def backward(self, text: str) -> str:
|
14
|
+
mac, text = text.split('-', 1)
|
15
|
+
if mac != self.__hmac(text):
|
16
|
+
raise ValueError('Invalid HMAC')
|
17
|
+
return text
|
18
|
+
|
19
|
+
def __hmac(self, text: str) -> str:
|
20
|
+
return hmac.new(self.__secret.encode('utf8'), text.encode('utf8'), sha256).hexdigest()
|
21
|
+
|
22
|
+
@property
|
23
|
+
def static(self) -> bool:
|
24
|
+
return True
|
dictature/transformer/mock.py
CHANGED
@@ -4,3 +4,6 @@ class MockTransformer:
|
|
4
4
|
raise NotImplementedError("This method should be implemented by the child class")
|
5
5
|
def backward(self, text: str) -> str:
|
6
6
|
raise NotImplementedError("This method should be implemented by the child class")
|
7
|
+
@property
|
8
|
+
def static(self) -> bool:
|
9
|
+
raise NotImplementedError("This method should be implemented by the child class")
|
@@ -0,0 +1,22 @@
|
|
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
|
+
self.__transformers = transformers
|
9
|
+
|
10
|
+
def forward(self, text: str) -> str:
|
11
|
+
for transformer in self.__transformers:
|
12
|
+
text = transformer.forward(text)
|
13
|
+
return text
|
14
|
+
|
15
|
+
def backward(self, text: str) -> str:
|
16
|
+
for transformer in reversed(self.__transformers):
|
17
|
+
text = transformer.backward(text)
|
18
|
+
return text
|
19
|
+
|
20
|
+
@property
|
21
|
+
def static(self) -> bool:
|
22
|
+
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.
|
3
|
+
Version: 0.9.3
|
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
|
@@ -0,0 +1,17 @@
|
|
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,,
|
dictature-0.9.1.dist-info/RECORD
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
dictature/__init__.py,sha256=UCPJKHeyirRZ0pCYoyeat-rwXa8pDezOJ3UWCipDdyc,33
|
2
|
-
dictature/dictature.py,sha256=-RQhAy_GNUK2oMzuS-Wk8n4yqCjxqlx_fgcQVAUNQ2k,5005
|
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=RyvkrqOIeM7NX7QqdKIJID-jd7kuKZnC6_Lp05IfY3k,1587
|
9
|
-
dictature/transformer/mock.py,sha256=p1yqlF-3igYDe_4JPZaqivOmOVTxU1xYiY16Mv73Aw8,287
|
10
|
-
dictature/transformer/passthrough.py,sha256=EhYPMwPHyWyjCzFjENunsZDuR9rb-4O7GB__rRgBMBA,205
|
11
|
-
dictature-0.9.1.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
|
12
|
-
dictature-0.9.1.dist-info/METADATA,sha256=gYo1CpukwPRh97y16IPYsdJkblF_Z_nFPC6HBpZUulM,2478
|
13
|
-
dictature-0.9.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
14
|
-
dictature-0.9.1.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
|
15
|
-
dictature-0.9.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|