dictature 0.9.7__py3-none-any.whl → 0.10.0__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.
@@ -0,0 +1,141 @@
1
+ import io
2
+ import posixpath
3
+ from typing import Iterable
4
+
5
+ try:
6
+ from webdav3.client import Client as WebdavClient
7
+ from webdav3.exceptions import (
8
+ WebDavException,
9
+ RemoteResourceNotFound,
10
+ RemoteParentNotFound,
11
+ MethodNotSupported,
12
+ )
13
+ except ImportError:
14
+ raise ImportError('Requires: pip install webdavclient3')
15
+
16
+ from .mock import DictatureTableMock, DictatureBackendMock, Value, ValueMode, ValueSerializer, ValueSerializerMode
17
+
18
+
19
+ class DictatureBackendWebdav(DictatureBackendMock):
20
+ def __init__(
21
+ self,
22
+ client: WebdavClient,
23
+ dir_prefix: str = 'db_',
24
+ item_prefix: str = 'item_'
25
+ ) -> None:
26
+ self.__client = client
27
+ self.__dir_prefix = dir_prefix
28
+ self.__item_prefix = item_prefix
29
+
30
+ def keys(self) -> Iterable[str]:
31
+ try:
32
+ resources = self.__client.list(get_info=True)
33
+
34
+ for resource_info in resources:
35
+ name = posixpath.basename(posixpath.dirname(resource_info.get('path')))
36
+ is_dir = resource_info.get('isdir', False)
37
+
38
+ if not name:
39
+ continue
40
+
41
+ if is_dir and name.startswith(self.__dir_prefix):
42
+ table_name_encoded = name[len(self.__dir_prefix):]
43
+ # noinspection PyProtectedMember
44
+ yield DictatureTableWebdav._filename_decode(table_name_encoded, suffix='')
45
+ except RemoteResourceNotFound:
46
+ pass
47
+
48
+ def table(self, name: str) -> 'DictatureTableMock':
49
+ return DictatureTableWebdav(self.__client, name, self.__dir_prefix, self.__item_prefix)
50
+
51
+
52
+ class DictatureTableWebdav(DictatureTableMock):
53
+ def __init__(self, client: WebdavClient, name: str, db_prefix: str, prefix: str) -> None:
54
+ self.__client = client
55
+ self.__encoded_name = self._filename_encode(name, suffix='')
56
+ self.__path = posixpath.join(db_prefix + self.__encoded_name)
57
+ self.__prefix = prefix
58
+ self.__name_serializer = ValueSerializer(mode=ValueSerializerMode.filename_only)
59
+ self.__value_serializer = ValueSerializer(mode=ValueSerializerMode.any_string)
60
+
61
+ def keys(self) -> Iterable[str]:
62
+ try:
63
+ resources = self.__client.list(self.__path, get_info=True)
64
+
65
+ for resource_info in resources:
66
+ name = posixpath.basename(resource_info.get('path'))
67
+ is_dir = resource_info.get('isdir', False)
68
+
69
+ if not name:
70
+ continue
71
+
72
+ if not is_dir and name.startswith(self.__prefix):
73
+ item_name_encoded = name[len(self.__prefix):]
74
+ yield self._filename_decode(item_name_encoded, suffix='.txt')
75
+
76
+ except RemoteResourceNotFound:
77
+ pass
78
+
79
+ def drop(self) -> None:
80
+ try:
81
+ self.__client.clean(self.__path)
82
+ except RemoteResourceNotFound:
83
+ pass
84
+
85
+ def create(self) -> None:
86
+ try:
87
+ if not self.__client.check(self.__path):
88
+ self.__client.mkdir(self.__path)
89
+ except MethodNotSupported as e:
90
+ if not self.__client.check(self.__path):
91
+ raise e
92
+
93
+ def set(self, item: str, value: Value) -> None:
94
+ item_path = self.__item_path(item)
95
+ self.create()
96
+
97
+ save_data: str = self.__value_serializer.serialize(value)
98
+ data_bytes = save_data.encode('utf-8')
99
+
100
+ with io.BytesIO(data_bytes) as buffer:
101
+ self.__client.upload_to(buffer, item_path)
102
+
103
+ def get(self, item: str) -> Value:
104
+ item_path = self.__item_path(item)
105
+ buffer = io.BytesIO()
106
+ try:
107
+ self.__client.download_from(buffer, item_path)
108
+ buffer.seek(0)
109
+ save_data = buffer.read().decode('utf-8')
110
+ except RemoteResourceNotFound:
111
+ raise KeyError(item)
112
+ return self.__value_serializer.deserialize(save_data)
113
+
114
+ def delete(self, item: str) -> None:
115
+ """
116
+ Deletes the file corresponding to the item. No error if not found.
117
+ Raises WebDavException for other deletion errors.
118
+ """
119
+ item_path = self.__item_path(item)
120
+ try:
121
+ self.__client.clean(item_path)
122
+ except RemoteResourceNotFound:
123
+ pass
124
+
125
+ def __item_path(self, item: str) -> str:
126
+ encoded_item_name = self.__prefix + self._filename_encode(item, suffix='.txt')
127
+ full_path = posixpath.join(self.__path, encoded_item_name)
128
+ return full_path
129
+
130
+ @staticmethod
131
+ def _filename_encode(name: str, suffix: str = '.txt') -> str:
132
+ return ValueSerializer(mode=ValueSerializerMode.filename_only).serialize(Value(
133
+ value=name,
134
+ mode=ValueMode.string.value
135
+ )) + suffix
136
+
137
+ @staticmethod
138
+ def _filename_decode(name: str, suffix: str = '.txt') -> str:
139
+ if suffix and name.endswith(suffix):
140
+ name = name[:-len(suffix)]
141
+ return ValueSerializer(mode=ValueSerializerMode.filename_only).deserialize(name).value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dictature
3
- Version: 0.9.7
3
+ Version: 0.10.0
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
@@ -54,6 +54,7 @@ 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
56
  - `DictatureBackendMISP`: stores the data in a MISP instance
57
+ - `DictatureBackendWebdav`: stores data in a WebDav share as files
57
58
 
58
59
  ### Transformers
59
60
 
@@ -5,6 +5,7 @@ dictature/backend/directory.py,sha256=nPNJ-wRZOsuzaCOLxgGnNB093qID5LWw7VELAqi7kZ
5
5
  dictature/backend/misp.py,sha256=iPjvgnJg6WveNP2wvgN7OK2vkX-SC9qYPrdoa9ahRT0,4411
6
6
  dictature/backend/mock.py,sha256=BzfLstxkTIjk6mcMTdFKj8rSaFgIqn9-2Cyelslj8bY,5889
7
7
  dictature/backend/sqlite.py,sha256=zyphYEeLY4eGuBCor16i80_-brdipMpXZ3_kONwErsE,5237
8
+ dictature/backend/webdav.py,sha256=Y-3_WTcMyKVUnsVjiUZAxuy10FK0Yr-7Mgn13clg3po,5039
8
9
  dictature/transformer/__init__.py,sha256=JIFJpXU6iB9hIUM8L7HL2o9Nqjm_YbMEuQBQC8ZJ6b4,124
9
10
  dictature/transformer/aes.py,sha256=ZhC1dT9QpnziErkDLriWLgXDEFNGQW0KG4aqSN2AZpA,1926
10
11
  dictature/transformer/gzip.py,sha256=pngvJQeALa-lv98VBeJ1Pl6_gaAfGcPXD9UR7PexrYA,1921
@@ -12,8 +13,8 @@ dictature/transformer/hmac.py,sha256=vURsB0HlzRPn_Vkl7lGmZV9OKempQuds8AanmadDxIo
12
13
  dictature/transformer/mock.py,sha256=7zu65ZqUV_AVRaPSzNd73cVMXixXt31SeuX9OKZxaJQ,948
13
14
  dictature/transformer/passthrough.py,sha256=Pt3N6G_Qh6HJ_q75ETL5nfAwYHLB-SjkVwUwbbbMik8,344
14
15
  dictature/transformer/pipeline.py,sha256=OaQaJeJ5NpICetJe08r8ontqstsXGuW8jDbKw1zxYs4,842
15
- dictature-0.9.7.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
16
- dictature-0.9.7.dist-info/METADATA,sha256=oyBWco6Qtzo3C6MsHl_tdco6Oqo0mTyIsyXeui7sfjo,2869
17
- dictature-0.9.7.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
18
- dictature-0.9.7.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
19
- dictature-0.9.7.dist-info/RECORD,,
16
+ dictature-0.10.0.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
17
+ dictature-0.10.0.dist-info/METADATA,sha256=rRiKQ1lciPHmU2f2iwhUxQGG_OOLPWBmp-njyl9pv2k,2937
18
+ dictature-0.10.0.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
19
+ dictature-0.10.0.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
20
+ dictature-0.10.0.dist-info/RECORD,,