hypern 0.3.7__cp312-cp312-win32.whl → 0.3.9__cp312-cp312-win32.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.
- hypern/__init__.py +21 -1
- hypern/application.py +29 -36
- hypern/args_parser.py +4 -23
- hypern/database/sqlalchemy/__init__.py +4 -0
- hypern/database/{addons/sqlalchemy/__init__.py → sqlalchemy/config.py} +0 -5
- hypern/database/{sql → sqlx}/query.py +1 -1
- hypern/datastructures.py +2 -2
- hypern/hypern.cp312-win32.pyd +0 -0
- hypern/hypern.pyi +3 -5
- hypern/worker.py +265 -21
- {hypern-0.3.7.dist-info → hypern-0.3.9.dist-info}/METADATA +16 -14
- {hypern-0.3.7.dist-info → hypern-0.3.9.dist-info}/RECORD +19 -36
- {hypern-0.3.7.dist-info → hypern-0.3.9.dist-info}/WHEEL +1 -1
- hypern/database/addons/__init__.py +0 -5
- hypern/database/addons/sqlalchemy/fields/__init__.py +0 -14
- hypern/database/addons/sqlalchemy/fields/color.py +0 -16
- hypern/database/addons/sqlalchemy/fields/daterange.py +0 -23
- hypern/database/addons/sqlalchemy/fields/datetime.py +0 -22
- hypern/database/addons/sqlalchemy/fields/encrypted.py +0 -58
- hypern/database/addons/sqlalchemy/fields/password.py +0 -171
- hypern/database/addons/sqlalchemy/fields/ts_vector.py +0 -46
- hypern/database/addons/sqlalchemy/fields/unicode.py +0 -15
- hypern/database/nosql/__init__.py +0 -25
- hypern/database/nosql/addons/__init__.py +0 -4
- hypern/database/nosql/addons/color.py +0 -16
- hypern/database/nosql/addons/daterange.py +0 -30
- hypern/database/nosql/addons/encrypted.py +0 -53
- hypern/database/nosql/addons/password.py +0 -134
- hypern/database/nosql/addons/unicode.py +0 -10
- hypern/security.py +0 -44
- hypern/ws.py +0 -16
- /hypern/database/{addons/sqlalchemy → sqlalchemy}/repository.py +0 -0
- /hypern/database/{sql → sqlx}/__init__.py +0 -0
- /hypern/database/{sql → sqlx}/field.py +0 -0
- /hypern/database/{sql → sqlx}/migrate.py +0 -0
- /hypern/database/{sql → sqlx}/model.py +0 -0
- {hypern-0.3.7.dist-info → hypern-0.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
|
|
1
|
-
hypern-0.3.
|
2
|
-
hypern-0.3.
|
3
|
-
hypern-0.3.
|
4
|
-
hypern/application.py,sha256=
|
5
|
-
hypern/args_parser.py,sha256=
|
1
|
+
hypern-0.3.9.dist-info/METADATA,sha256=So9YgdPdWF6zU6X3v8wRPflQyHZvCMJBMWHTriMz7SQ,4112
|
2
|
+
hypern-0.3.9.dist-info/WHEEL,sha256=19xj5Waw2omQTyAh5Pnrm7rXeZkfzX1OuBYghlKYN-I,92
|
3
|
+
hypern-0.3.9.dist-info/licenses/LICENSE,sha256=qbYKAIJLS6jYg5hYncKE7OtWmqOtpVTvKNkwOa0Iwwg,1328
|
4
|
+
hypern/application.py,sha256=EVa3bjUjgEmM7jDpGTVcFlBa7kk2m69QMlpdsp8AWpU,18068
|
5
|
+
hypern/args_parser.py,sha256=1v41RZT-35iY80I5OugjeFPXPr28HUvniQwu-29y1vw,2222
|
6
6
|
hypern/auth/authorization.py,sha256=-NprZsI0np889ZN1fp-MiVFrPoMNzUtatBJaCMtkllM,32
|
7
7
|
hypern/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
hypern/background.py,sha256=xy38nQZSJsYFRXr3-uFJeNW9E1GiXXOC7lSe5pC0eyE,124
|
@@ -13,31 +13,16 @@ hypern/caching/__init__.py,sha256=ODO7zMm4iFG8wcvrhKmukryG5wOTW0DnzFvNMfF57Cc,35
|
|
13
13
|
hypern/cli/commands.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
hypern/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
hypern/config.py,sha256=Jij9eGg5NgC8Un5Lw5i7ghuEMAfkVctdcoE4RaN5LTE,8157
|
16
|
-
hypern/database/
|
17
|
-
hypern/database/
|
18
|
-
hypern/database/
|
19
|
-
hypern/database/
|
20
|
-
hypern/database/
|
21
|
-
hypern/database/
|
22
|
-
hypern/database/
|
23
|
-
hypern/database/
|
24
|
-
hypern/database/addons/sqlalchemy/repository.py,sha256=ue6vWOTrnEPyDevlyh3v-7PU6GSfrZHYKrbXVuoS8UA,9516
|
25
|
-
hypern/database/addons/sqlalchemy/__init__.py,sha256=FuY78ubEwtifdQTVHhCrscYaAarlp2urgYBc_R77yt0,2766
|
26
|
-
hypern/database/addons/__init__.py,sha256=mdW0P0xvnK8htUk02ujvIaeHXl6w53JjrTS4ioNi1Bw,63
|
27
|
-
hypern/database/nosql/addons/color.py,sha256=bAGRuARCAYwZ1nO4jK0lzGYKmavTDtS34BxvrsetF74,446
|
28
|
-
hypern/database/nosql/addons/daterange.py,sha256=hGUSoVFqatNY-TB5wjZTq62iZpHpdsyRJIsHxsj1uDs,1192
|
29
|
-
hypern/database/nosql/addons/encrypted.py,sha256=B0M-uDqvZHVmIZcFdwcuC2MGsv0pGJFQ1lrOg8klR9U,1741
|
30
|
-
hypern/database/nosql/addons/password.py,sha256=jfZxvWFm6nV9EWpXq5Mj-jpqnl9QbokZj9WT14n7dKE,5035
|
31
|
-
hypern/database/nosql/addons/unicode.py,sha256=LaDpLfdoTcJuASPE-8fqOVD05H_uOx8gOdnyDn5Iu0c,268
|
32
|
-
hypern/database/nosql/addons/__init__.py,sha256=WEtPM8sPHilvga7zxwqvINeTkF0hdcfgPcAnHc4MASE,125
|
33
|
-
hypern/database/nosql/__init__.py,sha256=MH9YvlbRlbBCrQVNOdfTaK-hINwJxbJLmxwY9Mei7I8,644
|
34
|
-
hypern/database/sql/field.py,sha256=gV9u_BvMIoxoDT3_J7sL5XJNa5XFsAO9w324ThwHbNs,9121
|
35
|
-
hypern/database/sql/migrate.py,sha256=BTtAs3-iMyMDzIWl6B3rM9sj7XGggLDRjD0h_WgGPtc,9742
|
36
|
-
hypern/database/sql/model.py,sha256=C8_rJA1Adw1yPWthjmAGh26hjTBuwwlEdtH45ADxvL0,4044
|
37
|
-
hypern/database/sql/query.py,sha256=En19t27zt6iUDQbFgO_wLEWPQCkPeBuH3s37fzlhMVc,33345
|
38
|
-
hypern/database/sql/__init__.py,sha256=dbSAz2nP0DPKK4Bb_jJdObSaSYQfgZ8D4U1TJdc4e7c,645
|
16
|
+
hypern/database/sqlalchemy/config.py,sha256=3BKY8YoHKEvwdDulp1que6pqmyyzX3CgfrQHtDHZWeE,2646
|
17
|
+
hypern/database/sqlalchemy/repository.py,sha256=ue6vWOTrnEPyDevlyh3v-7PU6GSfrZHYKrbXVuoS8UA,9516
|
18
|
+
hypern/database/sqlalchemy/__init__.py,sha256=xOMrzdPyJBzwaZifxYg2XD3TSorCOBJOIKYbnbESTuo,154
|
19
|
+
hypern/database/sqlx/field.py,sha256=gV9u_BvMIoxoDT3_J7sL5XJNa5XFsAO9w324ThwHbNs,9121
|
20
|
+
hypern/database/sqlx/migrate.py,sha256=BTtAs3-iMyMDzIWl6B3rM9sj7XGggLDRjD0h_WgGPtc,9742
|
21
|
+
hypern/database/sqlx/model.py,sha256=C8_rJA1Adw1yPWthjmAGh26hjTBuwwlEdtH45ADxvL0,4044
|
22
|
+
hypern/database/sqlx/query.py,sha256=db4gKmw_Lvud2xUe97Kec886vLVMRkjoqFasSHxZMiA,33346
|
23
|
+
hypern/database/sqlx/__init__.py,sha256=dbSAz2nP0DPKK4Bb_jJdObSaSYQfgZ8D4U1TJdc4e7c,645
|
39
24
|
hypern/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
|
-
hypern/datastructures.py,sha256=
|
25
|
+
hypern/datastructures.py,sha256=Pxr9KsZZTFfp0KC1-A4v5AkQfmrUyvVwxKuToQUOLoE,882
|
41
26
|
hypern/enum.py,sha256=KcVziJj7vWvyie0r2rtxhrLzdtkZAsf0DY58oJ4tQl4,360
|
42
27
|
hypern/exceptions/base.py,sha256=5AgfyEea79JjKk5MeAIJ-wy44FG5XEU0Jn3KXKScPiI,2017
|
43
28
|
hypern/exceptions/common.py,sha256=0E8wHRRTWjYOmtOCkTDvZ5NMwL6vRW6aiDD9X1eYA30,227
|
@@ -50,7 +35,7 @@ hypern/gateway/gateway.py,sha256=26K2qvJUR-0JnN4IlhwvSSt7EYcpYrBVDuzZ1ivQQ34,147
|
|
50
35
|
hypern/gateway/proxy.py,sha256=w1wcTplDnVrfjn7hb0M0yBVth5TGl88irF-MUYHysQQ,2463
|
51
36
|
hypern/gateway/service.py,sha256=PkRaM08olqM_j_4wRjEJCR8X8ZysAF2WOcfhWjaX2eo,1701
|
52
37
|
hypern/gateway/__init__.py,sha256=TpFWtqnJerW1-jCWq5fjypJcw9Y6ytyrkvkzby1Eg0E,235
|
53
|
-
hypern/hypern.pyi,sha256=
|
38
|
+
hypern/hypern.pyi,sha256=C_RAN4E9m4FFGGTd3k6JVi59ORl_knLQtIHM1wqDlBg,9051
|
54
39
|
hypern/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
40
|
hypern/logging/logger.py,sha256=WACam_IJiCMXX0hGVKMGSxUQpY4DgAXy7M1dD3q-Z9s,3256
|
56
41
|
hypern/logging/__init__.py,sha256=6eVriyncsJ4J73fGYhoejv9MX7aGTkRezTpPxO4DX1I,52
|
@@ -77,14 +62,12 @@ hypern/routing/queue.py,sha256=NtFBbogU22ddyyX-CuQMip1XFDPZdMCVMIeUCQ-CR6Y,7176
|
|
77
62
|
hypern/routing/route.py,sha256=kan47-UeL-OPwcpp0rEhmBaaum6hN7FUj13Y8pZDEYA,10256
|
78
63
|
hypern/routing/__init__.py,sha256=U4xW5fDRsn03z4cVLT4dJHHGGU6SVxyv2DL86LXodeE,162
|
79
64
|
hypern/scheduler.py,sha256=-k3tW2AGCnHYSthKXk-FOs_SCtWp3yIxQzwzUJMJsbo,67
|
80
|
-
hypern/
|
81
|
-
hypern/worker.py,sha256=WQrhY_awR6zjMwY4Q7izXi4E4fFrDqt7jIblUW8Bzcg,924
|
65
|
+
hypern/worker.py,sha256=ksJW8jWQg3HbIYnIZ5qdAmO-yh5hLpwvTT3dKkHR4Eo,9761
|
82
66
|
hypern/ws/channel.py,sha256=0ns2qmeoFJOpGLXS_hqldhywDQm_DxHwj6KloQx4Q3I,3183
|
83
67
|
hypern/ws/heartbeat.py,sha256=sWMXzQm6cbDHHA2NHc-gFjv7G_E56XtxswHQ93_BueM,2861
|
84
68
|
hypern/ws/room.py,sha256=0_L6Nun0n007F0rfNY8yX5x_A8EuXuI67JqpMkJ4RNI,2598
|
85
69
|
hypern/ws/route.py,sha256=fGQ2RC708MPOiiIHPUo8aZ-oK379TTAyQYm4htNA5jM,803
|
86
70
|
hypern/ws/__init__.py,sha256=dhRoRY683_rfPfSPM5qUczfTuyYDeuLOCFxY4hIdKt8,131
|
87
|
-
hypern/
|
88
|
-
hypern/
|
89
|
-
hypern
|
90
|
-
hypern-0.3.7.dist-info/RECORD,,
|
71
|
+
hypern/__init__.py,sha256=p3AtJQbsPs1RYEiN1thxH-k5UP8FfLeiJSoP0Vt3MDg,639
|
72
|
+
hypern/hypern.cp312-win32.pyd,sha256=tBTocJzcSqw9JvAccQ99FQVqRLgaE9sRzGIHKpfpUrY,8263680
|
73
|
+
hypern-0.3.9.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
from .ts_vector import TSVector
|
3
|
-
from .datetime import DatetimeType
|
4
|
-
from .password import PasswordType
|
5
|
-
from .encrypted import StringEncryptType, LargeBinaryEncryptType, AESEngine
|
6
|
-
|
7
|
-
__all__ = [
|
8
|
-
"TSVector",
|
9
|
-
"DatetimeType",
|
10
|
-
"PasswordType",
|
11
|
-
"StringEncryptType",
|
12
|
-
"LargeBinaryEncryptType",
|
13
|
-
"AESEngine",
|
14
|
-
]
|
@@ -1,16 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
|
3
|
-
from sqlalchemy.types import String, TypeDecorator
|
4
|
-
|
5
|
-
|
6
|
-
class ColorField(TypeDecorator):
|
7
|
-
impl = String
|
8
|
-
|
9
|
-
def process_bind_param(self, value, dialect):
|
10
|
-
color_regex = r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
|
11
|
-
if not re.match(color_regex, value):
|
12
|
-
raise ValueError("Invalid color format. Use hexadecimal color codes (e.g., #FF0000)")
|
13
|
-
return value
|
14
|
-
|
15
|
-
def process_result_value(self, value, dialect):
|
16
|
-
return value
|
@@ -1,23 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from sqlalchemy.dialects.postgresql import DATERANGE
|
4
|
-
from sqlalchemy.types import TypeDecorator
|
5
|
-
|
6
|
-
|
7
|
-
class DateRangeField(TypeDecorator):
|
8
|
-
impl = DATERANGE
|
9
|
-
|
10
|
-
def process_bind_param(self, value, dialect):
|
11
|
-
if value is None:
|
12
|
-
return None
|
13
|
-
elif "start" in value and "end" in value:
|
14
|
-
return f"['{value['start']}', '{value['end']}']"
|
15
|
-
else:
|
16
|
-
raise ValueError('DateRangeField must be a dictionary with "start" and "end" keys')
|
17
|
-
|
18
|
-
def process_result_value(self, value, dialect):
|
19
|
-
if value is None:
|
20
|
-
return None
|
21
|
-
else:
|
22
|
-
start, end = value[1:-1].split(",")
|
23
|
-
return {"start": datetime.strptime(start.strip("'"), "%Y-%m-%d %H:%M:%S.%f"), "end": datetime.strptime(end.strip("'"), "%Y-%m-%d %H:%M:%S.%f")}
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
from sqlalchemy import types
|
3
|
-
|
4
|
-
|
5
|
-
class DatetimeType(types.TypeDecorator):
|
6
|
-
impl = types.DateTime
|
7
|
-
cache_ok = True
|
8
|
-
|
9
|
-
def load_dialect_impl(self, dialect):
|
10
|
-
if dialect.name == "sqlite":
|
11
|
-
return dialect.type_descriptor(types.TEXT)
|
12
|
-
return dialect.type_descriptor(self.impl)
|
13
|
-
|
14
|
-
def process_bind_param(self, value, dialect):
|
15
|
-
if dialect.name == "sqlite":
|
16
|
-
return value.isoformat()
|
17
|
-
return value
|
18
|
-
|
19
|
-
def process_result_value(self, value, dialect):
|
20
|
-
if dialect.name != "sqlite":
|
21
|
-
return value.timestamp()
|
22
|
-
return value
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
import os
|
3
|
-
import typing
|
4
|
-
|
5
|
-
from cryptography.hazmat.primitives import padding
|
6
|
-
from sqlalchemy.types import LargeBinary, String, TypeDecorator
|
7
|
-
|
8
|
-
from hypern.security import AESEngine, EDEngine
|
9
|
-
|
10
|
-
|
11
|
-
class StringEncryptType(TypeDecorator):
|
12
|
-
impl = String
|
13
|
-
cache_ok = True
|
14
|
-
|
15
|
-
def __init__(self, engine: typing.Optional[EDEngine] = None, *args, **kwargs) -> None:
|
16
|
-
super().__init__(*args, **kwargs)
|
17
|
-
|
18
|
-
if not engine:
|
19
|
-
key = os.urandom(32)
|
20
|
-
iv = os.urandom(16)
|
21
|
-
padding_class = padding.PKCS7
|
22
|
-
self.engine = AESEngine(secret_key=key, iv=iv, padding_class=padding_class)
|
23
|
-
else:
|
24
|
-
self.engine = engine # type: ignore
|
25
|
-
|
26
|
-
def process_bind_param(self, value, dialect):
|
27
|
-
if value is None:
|
28
|
-
return value
|
29
|
-
if not isinstance(value, str):
|
30
|
-
raise ValueError("Value String Encrypt Type must be a string")
|
31
|
-
return self.engine.encrypt(value).decode(encoding="utf-8")
|
32
|
-
|
33
|
-
def process_result_value(self, value, dialect):
|
34
|
-
if value is None:
|
35
|
-
return value
|
36
|
-
return self.engine.decrypt(value)
|
37
|
-
|
38
|
-
|
39
|
-
class LargeBinaryEncryptType(StringEncryptType):
|
40
|
-
impl = LargeBinary
|
41
|
-
cache_ok = True
|
42
|
-
|
43
|
-
def __init__(self, engine: typing.Optional[EDEngine] = None, *args, **kwargs) -> None:
|
44
|
-
super().__init__(engine=engine, *args, **kwargs) # type: ignore
|
45
|
-
|
46
|
-
def process_bind_param(self, value, dialect):
|
47
|
-
if value is None:
|
48
|
-
return value
|
49
|
-
value = super().process_bind_param(value, dialect)
|
50
|
-
if isinstance(value, str):
|
51
|
-
return value.encode("utf-8")
|
52
|
-
return value
|
53
|
-
|
54
|
-
def process_result_value(self, value, dialect):
|
55
|
-
if isinstance(value, bytes):
|
56
|
-
value = value.decode("utf-8")
|
57
|
-
return super().process_result_value(value, dialect)
|
58
|
-
return value
|
@@ -1,171 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
import weakref
|
4
|
-
|
5
|
-
import passlib
|
6
|
-
from passlib.context import LazyCryptContext
|
7
|
-
from sqlalchemy import types
|
8
|
-
from sqlalchemy.dialects import oracle, postgresql, sqlite
|
9
|
-
from sqlalchemy.ext.mutable import Mutable
|
10
|
-
|
11
|
-
|
12
|
-
class Password(Mutable):
|
13
|
-
@classmethod
|
14
|
-
def coerce(cls, key, value):
|
15
|
-
if isinstance(value, Password):
|
16
|
-
return value
|
17
|
-
|
18
|
-
if isinstance(value, (str, bytes)):
|
19
|
-
return cls(value, secret=True)
|
20
|
-
|
21
|
-
super().coerce(key, value)
|
22
|
-
|
23
|
-
def __init__(self, value, context=None, secret=False):
|
24
|
-
# Store the hash (if it is one).
|
25
|
-
self.hash = value if not secret else None
|
26
|
-
|
27
|
-
# Store the secret if we have one.
|
28
|
-
self.secret = value if secret else None
|
29
|
-
|
30
|
-
# The hash should be bytes.
|
31
|
-
if isinstance(self.hash, str):
|
32
|
-
self.hash = self.hash.encode("utf8")
|
33
|
-
|
34
|
-
# Save weakref of the password context (if we have one)
|
35
|
-
self.context = weakref.proxy(context) if context is not None else None
|
36
|
-
|
37
|
-
def __eq__(self, value):
|
38
|
-
if self.hash is None or value is None:
|
39
|
-
# Ensure that we don't continue comparison if one of us is None.
|
40
|
-
return self.hash is value
|
41
|
-
|
42
|
-
if isinstance(value, Password):
|
43
|
-
# Comparing 2 hashes isn't very useful; but this equality
|
44
|
-
# method breaks otherwise.
|
45
|
-
return value.hash == self.hash
|
46
|
-
|
47
|
-
if self.context is None:
|
48
|
-
# Compare 2 hashes again as we don't know how to validate.
|
49
|
-
return value == self
|
50
|
-
|
51
|
-
if isinstance(value, (str, bytes)):
|
52
|
-
valid, new = self.context.verify_and_update(value, self.hash)
|
53
|
-
if valid and new:
|
54
|
-
# New hash was calculated due to various reasons; stored one
|
55
|
-
# wasn't optimal, etc.
|
56
|
-
self.hash = new
|
57
|
-
|
58
|
-
# The hash should be bytes.
|
59
|
-
if isinstance(self.hash, str):
|
60
|
-
self.hash = self.hash.encode("utf8")
|
61
|
-
self.changed()
|
62
|
-
|
63
|
-
return valid
|
64
|
-
|
65
|
-
return False
|
66
|
-
|
67
|
-
def __ne__(self, value):
|
68
|
-
return self != value
|
69
|
-
|
70
|
-
|
71
|
-
class PasswordType(types.TypeDecorator):
|
72
|
-
impl = types.String
|
73
|
-
cache_ok = True
|
74
|
-
|
75
|
-
def __init__(self, max_length=None, **kwargs):
|
76
|
-
# Fail if passlib is not found.
|
77
|
-
if passlib is None:
|
78
|
-
raise ImportError("'passlib' is required to use 'PasswordType'")
|
79
|
-
|
80
|
-
# Construct the passlib crypt context.
|
81
|
-
self.context = LazyCryptContext(**kwargs)
|
82
|
-
self._max_length = max_length
|
83
|
-
|
84
|
-
@property
|
85
|
-
def hashing_method(self):
|
86
|
-
return "hash" if hasattr(self.context, "hash") else "encrypt"
|
87
|
-
|
88
|
-
@property
|
89
|
-
def max_length(self):
|
90
|
-
"""Get column length."""
|
91
|
-
if self._max_length is None:
|
92
|
-
self._max_length = self.calculate_max_length()
|
93
|
-
|
94
|
-
return self._max_length
|
95
|
-
|
96
|
-
def calculate_max_length(self):
|
97
|
-
# Calculate the largest possible encoded password.
|
98
|
-
# name + rounds + salt + hash + ($ * 4) of largest hash
|
99
|
-
max_lengths = [1024]
|
100
|
-
for name in self.context.schemes():
|
101
|
-
scheme = getattr(__import__("passlib.hash").hash, name)
|
102
|
-
length = 4 + len(scheme.name)
|
103
|
-
length += len(str(getattr(scheme, "max_rounds", "")))
|
104
|
-
length += getattr(scheme, "max_salt_size", 0) or 0
|
105
|
-
length += getattr(scheme, "encoded_checksum_size", scheme.checksum_size)
|
106
|
-
max_lengths.append(length)
|
107
|
-
|
108
|
-
# Return the maximum calculated max length.
|
109
|
-
return max(max_lengths)
|
110
|
-
|
111
|
-
def load_dialect_impl(self, dialect):
|
112
|
-
if dialect.name == "postgresql":
|
113
|
-
# Use a BYTEA type for postgresql.
|
114
|
-
impl = postgresql.BYTEA(self.max_length)
|
115
|
-
elif dialect.name == "oracle":
|
116
|
-
# Use a RAW type for oracle.
|
117
|
-
impl = oracle.RAW(self.max_length)
|
118
|
-
elif dialect.name == "sqlite":
|
119
|
-
# Use a BLOB type for sqlite
|
120
|
-
impl = sqlite.BLOB(self.max_length)
|
121
|
-
else:
|
122
|
-
# Use a VARBINARY for all other dialects.
|
123
|
-
impl = types.VARBINARY(self.max_length)
|
124
|
-
return dialect.type_descriptor(impl)
|
125
|
-
|
126
|
-
def process_bind_param(self, value, dialect):
|
127
|
-
if isinstance(value, Password):
|
128
|
-
# If were given a password secret; hash it.
|
129
|
-
if value.secret is not None:
|
130
|
-
return self._hash(value.secret).encode("utf8")
|
131
|
-
|
132
|
-
# Value has already been hashed.
|
133
|
-
return value.hash
|
134
|
-
|
135
|
-
if isinstance(value, str):
|
136
|
-
# Assume value has not been hashed.
|
137
|
-
return self._hash(value).encode("utf8")
|
138
|
-
|
139
|
-
def process_result_value(self, value, dialect):
|
140
|
-
if value is not None:
|
141
|
-
return Password(value, self.context)
|
142
|
-
|
143
|
-
def _hash(self, value):
|
144
|
-
return getattr(self.context, self.hashing_method)(value)
|
145
|
-
|
146
|
-
def _coerce(self, value):
|
147
|
-
if value is None:
|
148
|
-
return
|
149
|
-
|
150
|
-
if not isinstance(value, Password):
|
151
|
-
# Hash the password using the default scheme.
|
152
|
-
value = self._hash(value).encode("utf8")
|
153
|
-
return Password(value, context=self.context)
|
154
|
-
|
155
|
-
else:
|
156
|
-
# If were given a password object; ensure the context is right.
|
157
|
-
value.context = weakref.proxy(self.context)
|
158
|
-
|
159
|
-
# If were given a password secret; hash it.
|
160
|
-
if value.secret is not None:
|
161
|
-
value.hash = self._hash(value.secret).encode("utf8")
|
162
|
-
value.secret = None
|
163
|
-
|
164
|
-
return value
|
165
|
-
|
166
|
-
@property
|
167
|
-
def python_type(self):
|
168
|
-
return self.impl.type.python_type
|
169
|
-
|
170
|
-
|
171
|
-
Password.associate_with(PasswordType)
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
import sqlalchemy as sa
|
4
|
-
from sqlalchemy.dialects.postgresql import TSVECTOR
|
5
|
-
|
6
|
-
|
7
|
-
class TSVector(sa.types.TypeDecorator):
|
8
|
-
"""
|
9
|
-
.. _TSVECTOR:
|
10
|
-
https://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#full-text-search
|
11
|
-
|
12
|
-
|
13
|
-
class IndexModel(Model):
|
14
|
-
....
|
15
|
-
search_vector = Column(
|
16
|
-
TSVector(),
|
17
|
-
Computed(
|
18
|
-
"to_tsvector('english', some_text|| ' ' ||some_text)",
|
19
|
-
persisted=True,
|
20
|
-
),
|
21
|
-
)
|
22
|
-
session.query(IndexModel).filter(IndexModel.search_vector.match('foo'))
|
23
|
-
|
24
|
-
session.query(IndexModel).filter(
|
25
|
-
(IndexModel.name_vector | IndexModel.content_vector).match('foo')
|
26
|
-
)
|
27
|
-
|
28
|
-
"""
|
29
|
-
|
30
|
-
impl = TSVECTOR
|
31
|
-
cache_ok = True
|
32
|
-
|
33
|
-
class comparator_factory(TSVECTOR.Comparator):
|
34
|
-
def match(self, other, **kwargs):
|
35
|
-
if "postgresql_regconfig" not in kwargs:
|
36
|
-
if "regconfig" in self.type.options:
|
37
|
-
kwargs["postgresql_regconfig"] = self.type.options["regconfig"]
|
38
|
-
return TSVECTOR.Comparator.match(self, other, **kwargs)
|
39
|
-
|
40
|
-
def __or__(self, other):
|
41
|
-
return self.op("||")(other)
|
42
|
-
|
43
|
-
def __init__(self, *args, **kwargs):
|
44
|
-
self.columns = args
|
45
|
-
self.options = kwargs
|
46
|
-
super(TSVector, self).__init__()
|
@@ -1,15 +0,0 @@
|
|
1
|
-
from sqlalchemy.types import TypeDecorator, Unicode
|
2
|
-
|
3
|
-
|
4
|
-
class UnicodeField(TypeDecorator):
|
5
|
-
impl = Unicode
|
6
|
-
|
7
|
-
def process_bind_param(self, value, dialect):
|
8
|
-
try:
|
9
|
-
value.encode("utf-8")
|
10
|
-
except UnicodeEncodeError:
|
11
|
-
raise ValueError("Value must be valid Unicode")
|
12
|
-
return value
|
13
|
-
|
14
|
-
def process_result_value(self, value, dialect):
|
15
|
-
return value
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
from typing import List, TypedDict
|
3
|
-
|
4
|
-
from uuid import uuid4
|
5
|
-
from mongoengine import connect
|
6
|
-
|
7
|
-
|
8
|
-
class TypedDictModel(TypedDict):
|
9
|
-
host: str
|
10
|
-
alias: str
|
11
|
-
|
12
|
-
|
13
|
-
class NoSqlConfig:
|
14
|
-
def __init__(self, dbs_config: List[TypedDictModel]):
|
15
|
-
self.dbs_config = dbs_config
|
16
|
-
|
17
|
-
def _connect_db(self, db_config: TypedDictModel):
|
18
|
-
_alias = db_config.get("alias", str(uuid4()))
|
19
|
-
connect(host=db_config["host"], alias=_alias)
|
20
|
-
|
21
|
-
def init_app(self, app):
|
22
|
-
self.app = app # noqa
|
23
|
-
# connect
|
24
|
-
for db_config in self.dbs_config:
|
25
|
-
self._connect_db(db_config)
|
@@ -1,16 +0,0 @@
|
|
1
|
-
from mongoengine import BaseField
|
2
|
-
import re
|
3
|
-
|
4
|
-
|
5
|
-
class ColorField(BaseField):
|
6
|
-
def validate(self, value):
|
7
|
-
color_regex = r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
|
8
|
-
if not re.match(color_regex, value):
|
9
|
-
self.error("Invalid color format. Use hexadecimal color codes (e.g., #FF0000)")
|
10
|
-
return True
|
11
|
-
|
12
|
-
def to_mongo(self, value):
|
13
|
-
return value
|
14
|
-
|
15
|
-
def to_python(self, value):
|
16
|
-
return value
|
@@ -1,30 +0,0 @@
|
|
1
|
-
from mongoengine import BaseField
|
2
|
-
from datetime import datetime
|
3
|
-
|
4
|
-
|
5
|
-
class DateRangeField(BaseField):
|
6
|
-
def __init__(self, **kwargs):
|
7
|
-
super(DateRangeField, self).__init__(**kwargs)
|
8
|
-
|
9
|
-
def validate(self, value):
|
10
|
-
if not isinstance(value, dict) or "start" not in value or "end" not in value:
|
11
|
-
self.error('DateRangeField must be a dictionary with "start" and "end" keys')
|
12
|
-
# Use get to safely access keys
|
13
|
-
start = value.get("start")
|
14
|
-
end = value.get("end")
|
15
|
-
# Check if both "start" and "end" are present
|
16
|
-
if start is None or end is None:
|
17
|
-
self.error('DateRangeField must contain both "start" and "end" keys')
|
18
|
-
|
19
|
-
# Check if "start" and "end" are datetime objects
|
20
|
-
if not isinstance(value["start"], datetime) or not isinstance(value["end"], datetime):
|
21
|
-
self.error('DateRangeField "start" and "end" must be datetime objects')
|
22
|
-
if value["start"] > value["end"]:
|
23
|
-
self.error('DateRangeField "start" must be earlier than "end"')
|
24
|
-
return True
|
25
|
-
|
26
|
-
def to_mongo(self, value):
|
27
|
-
return value
|
28
|
-
|
29
|
-
def to_python(self, value):
|
30
|
-
return value
|
@@ -1,53 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from typing import Any, Optional
|
3
|
-
from mongoengine.base import BaseField
|
4
|
-
|
5
|
-
from cryptography.hazmat.primitives import padding
|
6
|
-
|
7
|
-
from hypern.security import EDEngine, AESEngine
|
8
|
-
|
9
|
-
|
10
|
-
class EncryptedField(BaseField):
|
11
|
-
"""
|
12
|
-
A custom MongoEngine field that encrypts data using AES-256-CBC.
|
13
|
-
|
14
|
-
The field automatically handles encryption when saving to MongoDB and
|
15
|
-
decryption when retrieving data.
|
16
|
-
|
17
|
-
Attributes:
|
18
|
-
engine: Encryption engine to use. If not provided, will use AES-256-CBC
|
19
|
-
"""
|
20
|
-
|
21
|
-
def __init__(self, engine: Optional[EDEngine] = None, **kwargs):
|
22
|
-
if not engine:
|
23
|
-
key = os.urandom(32)
|
24
|
-
iv = os.urandom(16)
|
25
|
-
padding_class = padding.PKCS7
|
26
|
-
self.engine = AESEngine(secret_key=key, iv=iv, padding_class=padding_class)
|
27
|
-
else:
|
28
|
-
self.engine = engine # type: ignore
|
29
|
-
super(EncryptedField, self).__init__(**kwargs)
|
30
|
-
|
31
|
-
def to_mongo(self, value: Any) -> Optional[str]:
|
32
|
-
"""Convert a Python object to a MongoDB-compatible format."""
|
33
|
-
if value is None:
|
34
|
-
return None
|
35
|
-
return self.engine.encrypt(value)
|
36
|
-
|
37
|
-
def to_python(self, value: Optional[str]) -> Optional[str]:
|
38
|
-
"""Convert a MongoDB-compatible format to a Python object."""
|
39
|
-
if value is None:
|
40
|
-
return None
|
41
|
-
if isinstance(value, bytes):
|
42
|
-
return self.engine.decrypt(value)
|
43
|
-
return value
|
44
|
-
|
45
|
-
def prepare_query_value(self, op, value: Any) -> Optional[str]:
|
46
|
-
"""Prepare a value used in a query."""
|
47
|
-
if value is None:
|
48
|
-
return None
|
49
|
-
|
50
|
-
if op in ("set", "upsert"):
|
51
|
-
return self.to_mongo(value)
|
52
|
-
|
53
|
-
return value
|