dictature 0.12.1__py3-none-any.whl → 0.13.1__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,119 @@
1
+ try:
2
+ import mysql.connector
3
+ except ImportError:
4
+ raise ImportError('Requires: pip install mysql-connector-python') from None
5
+ from typing import Iterable
6
+
7
+ from .mock import DictatureBackendMock, DictatureTableMock, Value
8
+
9
+
10
+ class DictatureBackendMySQL(DictatureBackendMock):
11
+ def __init__(self, host: str, port: int = 3306, user: str = None, password: str = None,
12
+ database: str = None, prefix: str = 'tb_', **kwargs) -> None:
13
+ """
14
+ Create a new MySQL backend
15
+ :param host: MySQL server host
16
+ :param port: MySQL server port (default: 3306)
17
+ :param user: MySQL username
18
+ :param password: MySQL password
19
+ :param database: MySQL database name
20
+ :param kwargs: Additional connection parameters
21
+ """
22
+ self.__connection_params = {
23
+ 'host': host,
24
+ 'port': port,
25
+ 'user': user,
26
+ 'password': password,
27
+ 'database': database,
28
+ **kwargs
29
+ }
30
+
31
+ # Remove None values from connection params
32
+ self.__connection_params = {k: v for k, v in self.__connection_params.items() if v is not None}
33
+
34
+ self.__connection = mysql.connector.connect(**self.__connection_params)
35
+ self.__cursor = self.__connection.cursor()
36
+ self.__prefix = prefix.replace('`', '').replace("'", '')
37
+
38
+ def keys(self) -> Iterable[str]:
39
+ # noinspection SqlResolve
40
+ tables = self._execute(f"SELECT table_name FROM information_schema.tables WHERE table_schema = %s AND table_name LIKE '{self.__prefix}%'", (self.__connection_params['database'],))
41
+ return {table[0][3:] for table in tables}
42
+
43
+ def table(self, name: str) -> 'DictatureTableMock':
44
+ return DictatureTableMySQL(self, name, self.__prefix)
45
+
46
+ def _execute(self, query: str, data: tuple = ()) -> list:
47
+ self.__cursor.execute(query, data)
48
+ return self.__cursor.fetchall()
49
+
50
+ def _commit(self) -> None:
51
+ self.__connection.commit()
52
+
53
+ def __del__(self):
54
+ if hasattr(self, '_DictatureBackendMySQL__connection'):
55
+ self.__connection.close()
56
+
57
+
58
+ class DictatureTableMySQL(DictatureTableMock):
59
+ def __init__(self, parent: "DictatureBackendMySQL", name: str, prefix: str) -> None:
60
+ self.__parent = parent
61
+ # MySQL table names don't need backticks for simple names, but we'll use them for consistency
62
+ self.__table = f"`{prefix}{name.replace('`', '')}`"
63
+
64
+ def keys(self) -> Iterable[str]:
65
+ # noinspection PyProtectedMember
66
+ result = self.__parent._execute(f"SELECT `key` FROM {self.__table}")
67
+ return set(map(lambda x: x[0], result))
68
+
69
+ def drop(self) -> None:
70
+ # noinspection PyProtectedMember
71
+ self.__parent._execute(f"DROP TABLE {self.__table}")
72
+
73
+ def key_exists(self, item: str) -> bool:
74
+ # noinspection PyProtectedMember
75
+ result = self.__parent._execute(f"SELECT `value` FROM {self.__table} WHERE `key`=%s", (item,))
76
+ return len(result) > 0
77
+
78
+ def create(self) -> None:
79
+ # noinspection PyProtectedMember
80
+ self.__parent._execute(f"""
81
+ CREATE TABLE IF NOT EXISTS {self.__table} (
82
+ `key` VARCHAR(700) NOT NULL UNIQUE,
83
+ `value` TEXT,
84
+ `type` INT NOT NULL DEFAULT 0,
85
+ PRIMARY KEY (`key`)
86
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
87
+ """)
88
+
89
+ def set(self, item: str, value: Value) -> None:
90
+ if self.key_exists(item):
91
+ # noinspection PyProtectedMember
92
+ self.__parent._execute(f"""
93
+ UPDATE {self.__table} SET `value`=%s, `type`=%s WHERE `key`=%s
94
+ """, (value.value, value.mode, item))
95
+ else:
96
+ # noinspection PyProtectedMember
97
+ self.__parent._execute(
98
+ f"INSERT INTO {self.__table} (`key`, `value`, `type`) VALUES (%s, %s, %s)",
99
+ (item, value.value, value.mode)
100
+ )
101
+ # noinspection PyProtectedMember
102
+ self.__parent._commit()
103
+
104
+ def get(self, item: str) -> Value:
105
+ # noinspection PyProtectedMember
106
+ r = self.__parent._execute(f"SELECT `value`, `type` FROM {self.__table} WHERE `key`=%s", (item,))
107
+ if r:
108
+ value: str = r[0][0]
109
+ type_value: int = r[0][1]
110
+ return Value(value=value, mode=type_value)
111
+ raise KeyError(item)
112
+
113
+ def delete(self, item: str) -> None:
114
+ # noinspection PyProtectedMember
115
+ self.__parent._execute(
116
+ f"DELETE FROM {self.__table} WHERE `key`=%s", (item,)
117
+ )
118
+ # noinspection PyProtectedMember
119
+ self.__parent._commit()
@@ -1,15 +1,16 @@
1
1
  import sqlite3
2
- from typing import Iterable, Union, Optional
3
2
  from pathlib import Path
3
+ from typing import Iterable, Union, Optional
4
4
 
5
5
  from .mock import DictatureBackendMock, DictatureTableMock, Value, ValueMode
6
6
 
7
7
 
8
8
  class DictatureBackendSQLite(DictatureBackendMock):
9
- def __init__(self, file: Union[str, Path]) -> None:
9
+ def __init__(self, file: Union[str, Path], prefix: str = 'tb_') -> None:
10
10
  """
11
11
  Create a new SQLite backend
12
12
  :param file: file to store the database
13
+ :param prefix: prefix for the tables (default: 'tb_')
13
14
  """
14
15
  if isinstance(file, str):
15
16
  file = Path(file)
@@ -19,13 +20,14 @@ class DictatureBackendSQLite(DictatureBackendMock):
19
20
  check_same_thread=False if sqlite3.threadsafety >= 3 else True
20
21
  )
21
22
  self.__cursor = self.__connection.cursor()
23
+ self.__prefix = prefix.replace("'", "").replace('`', '')
22
24
 
23
25
  def keys(self) -> Iterable[str]:
24
- tables = self._execute("SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name LIKE 'tb_%'")
26
+ tables = self._execute(f"SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name LIKE '{self.__prefix}%'")
25
27
  return {table[0][3:] for table in tables}
26
28
 
27
29
  def table(self, name: str) -> 'DictatureTableMock':
28
- return DictatureTableSQLite(self, name)
30
+ return DictatureTableSQLite(self, name, self.__prefix)
29
31
 
30
32
  def _execute(self, query: str, data: tuple = ()) -> list:
31
33
  return list(self.__cursor.execute(query, data))
@@ -38,10 +40,10 @@ class DictatureBackendSQLite(DictatureBackendMock):
38
40
 
39
41
 
40
42
  class DictatureTableSQLite(DictatureTableMock):
41
- def __init__(self, parent: "DictatureBackendSQLite", name: str) -> None:
43
+ def __init__(self, parent: "DictatureBackendSQLite", name: str, prefix: str) -> None:
42
44
  self.__parent = parent
43
45
  self.__supports_jsonization: Optional[bool] = None
44
- self.__table = "`tb_%s`" % name.replace('`', '``')
46
+ self.__table = "`%s`" % (prefix + name).replace('`', '``')
45
47
 
46
48
  def keys(self) -> Iterable[str]:
47
49
  # noinspection PyProtectedMember
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dictature
3
- Version: 0.12.1
3
+ Version: 0.13.1
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
@@ -61,6 +61,7 @@ Currently, the following backends are supported:
61
61
  - `DictatureBackendMISP`: stores the data in a MISP instance
62
62
  - `DictatureBackendWebdav`: stores data in a WebDav share as files
63
63
  - `DictatureBackendS3`: stores data in an S3 bucket
64
+ - `DictatureBackendMySQL`: stores data in a MySQL database
64
65
 
65
66
  ### Transformers
66
67
 
@@ -4,8 +4,9 @@ dictature/backend/__init__.py,sha256=d5s6QCJOUzFglVNg8Cqqx_8b61S-AOTGjEUIF6FS69U
4
4
  dictature/backend/directory.py,sha256=_-8Fx7Ef4tFbOQWHcARfKjm0kbZUpmN1f1ysyWINOr4,3600
5
5
  dictature/backend/misp.py,sha256=R9osuEJa79SoXErwByIwhViJ26cEfw2nKhWu8XxkO7Y,4860
6
6
  dictature/backend/mock.py,sha256=o4YBl6Wk-q6IHFdykGeryTKcQFsJHvymlONPbbwVkak,5891
7
+ dictature/backend/mysql.py,sha256=LdbcytXCnu2pqx3LfOH__-dFy85IwB-HGMFOxDIGBvA,4624
7
8
  dictature/backend/s3.py,sha256=9Vcf4-RagyjpV9F4m9ZQ1vjb4lbZfEcOZQBuD_SPwZg,6580
8
- dictature/backend/sqlite.py,sha256=zyphYEeLY4eGuBCor16i80_-brdipMpXZ3_kONwErsE,5237
9
+ dictature/backend/sqlite.py,sha256=mHEfRufqRcDfl0mIPl9eK0ODyUmgYD9SWjbllI87Hp0,5434
9
10
  dictature/backend/webdav.py,sha256=Y-3_WTcMyKVUnsVjiUZAxuy10FK0Yr-7Mgn13clg3po,5039
10
11
  dictature/transformer/__init__.py,sha256=JIFJpXU6iB9hIUM8L7HL2o9Nqjm_YbMEuQBQC8ZJ6b4,124
11
12
  dictature/transformer/aes.py,sha256=ZhC1dT9QpnziErkDLriWLgXDEFNGQW0KG4aqSN2AZpA,1926
@@ -14,8 +15,8 @@ dictature/transformer/hmac.py,sha256=vURsB0HlzRPn_Vkl7lGmZV9OKempQuds8AanmadDxIo
14
15
  dictature/transformer/mock.py,sha256=7zu65ZqUV_AVRaPSzNd73cVMXixXt31SeuX9OKZxaJQ,948
15
16
  dictature/transformer/passthrough.py,sha256=Pt3N6G_Qh6HJ_q75ETL5nfAwYHLB-SjkVwUwbbbMik8,344
16
17
  dictature/transformer/pipeline.py,sha256=OaQaJeJ5NpICetJe08r8ontqstsXGuW8jDbKw1zxYs4,842
17
- dictature-0.12.1.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
18
- dictature-0.12.1.dist-info/METADATA,sha256=FDWISHrqfqU3SooFoZ8xRyAQ2IF8ryNGbKZYed3cJyE,3335
19
- dictature-0.12.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
20
- dictature-0.12.1.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
21
- dictature-0.12.1.dist-info/RECORD,,
18
+ dictature-0.13.1.dist-info/LICENSE,sha256=n1U9DKr8sM5EY2QHcvxSGiKTDWUT8MyXsOC79w94MT0,1072
19
+ dictature-0.13.1.dist-info/METADATA,sha256=hJZ0-CPWRgnNPvMY2l3uhPiEO9-xuNDgSwUmVM0adfs,3394
20
+ dictature-0.13.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
21
+ dictature-0.13.1.dist-info/top_level.txt,sha256=-RO39WWCF44lqiXhSUcACVqbk6SkgReZTz7ZmHKH3-U,10
22
+ dictature-0.13.1.dist-info/RECORD,,