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,134 +0,0 @@
|
|
1
|
-
from mongoengine.base import BaseField
|
2
|
-
import weakref
|
3
|
-
from passlib.context import CryptContext
|
4
|
-
import re
|
5
|
-
from typing import Optional, Any
|
6
|
-
|
7
|
-
|
8
|
-
class PasswordField(BaseField):
|
9
|
-
"""
|
10
|
-
A custom password field using passlib for hashing and weakref for reference management.
|
11
|
-
Supports multiple hashing schemes and automatic upgrade of hash algorithms.
|
12
|
-
"""
|
13
|
-
|
14
|
-
# Class-level password context - shared across all instances
|
15
|
-
pwd_context = CryptContext(
|
16
|
-
# List of hashing schemes in order of preference
|
17
|
-
schemes=["argon2", "pbkdf2_sha256", "bcrypt_sha256"],
|
18
|
-
# Mark argon2 as default
|
19
|
-
default="argon2",
|
20
|
-
# Argon2 parameters
|
21
|
-
argon2__rounds=4,
|
22
|
-
argon2__memory_cost=65536,
|
23
|
-
argon2__parallelism=2,
|
24
|
-
# PBKDF2 parameters
|
25
|
-
pbkdf2_sha256__rounds=29000,
|
26
|
-
)
|
27
|
-
|
28
|
-
def __init__(
|
29
|
-
self,
|
30
|
-
min_length: int = 8,
|
31
|
-
require_number: bool = False,
|
32
|
-
require_special: bool = False,
|
33
|
-
require_uppercase: bool = False,
|
34
|
-
require_lowercase: bool = False,
|
35
|
-
**kwargs,
|
36
|
-
):
|
37
|
-
"""
|
38
|
-
Initialize the password field with validation rules.
|
39
|
-
|
40
|
-
Args:
|
41
|
-
min_length: Minimum password length
|
42
|
-
require_number: Require at least one number
|
43
|
-
require_special: Require at least one special character
|
44
|
-
require_uppercase: Require at least one uppercase letter
|
45
|
-
require_lowercase: Require at least one lowercase letter
|
46
|
-
"""
|
47
|
-
self.min_length = min_length
|
48
|
-
self.require_number = require_number
|
49
|
-
self.require_special = require_special
|
50
|
-
self.require_uppercase = require_uppercase
|
51
|
-
self.require_lowercase = require_lowercase
|
52
|
-
|
53
|
-
# Use weakref to store references to parent documents
|
54
|
-
self.instances = weakref.WeakKeyDictionary()
|
55
|
-
|
56
|
-
kwargs["required"] = True
|
57
|
-
super(PasswordField, self).__init__(**kwargs)
|
58
|
-
|
59
|
-
def validate_password(self, password: str) -> tuple[bool, str]:
|
60
|
-
"""Validate password strength."""
|
61
|
-
|
62
|
-
if len(password) < self.min_length:
|
63
|
-
return False, f"Password must be at least {self.min_length} characters long"
|
64
|
-
|
65
|
-
if self.require_number and not re.search(r"\d", password):
|
66
|
-
return False, "Password must contain at least one number"
|
67
|
-
|
68
|
-
if self.require_special and not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
|
69
|
-
return False, "Password must contain at least one special character"
|
70
|
-
|
71
|
-
if self.require_uppercase and not re.search(r"[A-Z]", password):
|
72
|
-
return False, "Password must contain at least one uppercase letter"
|
73
|
-
|
74
|
-
if self.require_lowercase and not re.search(r"[a-z]", password):
|
75
|
-
return False, "Password must contain at least one lowercase letter"
|
76
|
-
|
77
|
-
return True, ""
|
78
|
-
|
79
|
-
def hash_password(self, password: str) -> str:
|
80
|
-
"""Hash password using the configured passlib context."""
|
81
|
-
return self.pwd_context.hash(password)
|
82
|
-
|
83
|
-
def verify_password(self, password: str, hash: str) -> tuple[bool, Optional[str]]:
|
84
|
-
"""
|
85
|
-
Verify password and return tuple of (is_valid, new_hash).
|
86
|
-
new_hash is provided if the hash needs to be upgraded.
|
87
|
-
"""
|
88
|
-
try:
|
89
|
-
is_valid = self.pwd_context.verify(password, hash)
|
90
|
-
# Check if the hash needs to be upgraded
|
91
|
-
if is_valid and self.pwd_context.needs_update(hash):
|
92
|
-
return True, self.hash_password(password)
|
93
|
-
return is_valid, None
|
94
|
-
except Exception:
|
95
|
-
return False, None
|
96
|
-
|
97
|
-
def __get__(self, instance, owner):
|
98
|
-
"""Custom getter using weakref."""
|
99
|
-
if instance is None:
|
100
|
-
return self
|
101
|
-
return self.instances.get(instance)
|
102
|
-
|
103
|
-
def __set__(self, instance, value):
|
104
|
-
"""Custom setter using weakref."""
|
105
|
-
if value and isinstance(value, str):
|
106
|
-
# Validate and hash new password
|
107
|
-
is_valid, error = self.validate_password(value)
|
108
|
-
if not is_valid:
|
109
|
-
raise ValueError(error)
|
110
|
-
hashed = self.hash_password(value)
|
111
|
-
self.instances[instance] = hashed
|
112
|
-
instance._data[self.name] = hashed
|
113
|
-
else:
|
114
|
-
# If it's already hashed or None
|
115
|
-
self.instances[instance] = value
|
116
|
-
instance._data[self.name] = value
|
117
|
-
|
118
|
-
def to_mongo(self, value: str) -> Optional[str]:
|
119
|
-
"""Convert to MongoDB-compatible value."""
|
120
|
-
if value is None:
|
121
|
-
return None
|
122
|
-
return self.hash_password(value)
|
123
|
-
|
124
|
-
def to_python(self, value: str) -> str:
|
125
|
-
"""Convert from MongoDB to Python."""
|
126
|
-
return value
|
127
|
-
|
128
|
-
def prepare_query_value(self, op, value: Any) -> Optional[str]:
|
129
|
-
"""Prepare value for database operations."""
|
130
|
-
if value is None:
|
131
|
-
return None
|
132
|
-
if op == "exact":
|
133
|
-
return self.hash_password(value)
|
134
|
-
return value
|
hypern/security.py
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
from abc import ABC, abstractmethod
|
3
|
-
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
4
|
-
from cryptography.hazmat.backends import default_backend
|
5
|
-
from base64 import b64encode, b64decode
|
6
|
-
|
7
|
-
import typing
|
8
|
-
|
9
|
-
|
10
|
-
class EDEngine(ABC):
|
11
|
-
@abstractmethod
|
12
|
-
def encrypt(self, data: str) -> str:
|
13
|
-
raise NotImplementedError("Method not implemented")
|
14
|
-
|
15
|
-
@abstractmethod
|
16
|
-
def decrypt(self, data: str) -> str:
|
17
|
-
raise NotImplementedError("Method not implemented")
|
18
|
-
|
19
|
-
|
20
|
-
class AESEngine(EDEngine):
|
21
|
-
def __init__(self, secret_key: bytes, iv: bytes, padding_class: typing.Type) -> None:
|
22
|
-
super().__init__()
|
23
|
-
self.secret_key = secret_key
|
24
|
-
self.iv = iv
|
25
|
-
self.padding = padding_class(128)
|
26
|
-
|
27
|
-
def encrypt(self, data: str) -> bytes:
|
28
|
-
bytes_data = data.encode("utf-8")
|
29
|
-
encryptor = Cipher(algorithms.AES(self.secret_key), modes.GCM(self.iv), backend=default_backend()).encryptor()
|
30
|
-
padder = self.padding.padder()
|
31
|
-
padded_data = padder.update(bytes_data) + padder.finalize()
|
32
|
-
enctyped_data = encryptor.update(padded_data) + encryptor.finalize()
|
33
|
-
tag = encryptor.tag
|
34
|
-
return b64encode(tag + enctyped_data)
|
35
|
-
|
36
|
-
def decrypt(self, data: bytes) -> str:
|
37
|
-
data = b64decode(data)
|
38
|
-
tag = data[:16]
|
39
|
-
encrypted_data = data[16:]
|
40
|
-
decryptor = Cipher(algorithms.AES(self.secret_key), modes.GCM(self.iv, tag), backend=default_backend()).decryptor()
|
41
|
-
unpadder = self.padding.unpadder()
|
42
|
-
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
43
|
-
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
|
44
|
-
return unpadded_data.decode("utf-8")
|
hypern/ws.py
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
from .hypern import WebsocketRoute as WebsocketRouteInternal, WebSocketSession
|
2
|
-
|
3
|
-
|
4
|
-
class WebsocketRoute:
|
5
|
-
def __init__(self) -> None:
|
6
|
-
self.routes = []
|
7
|
-
|
8
|
-
def on(self, path):
|
9
|
-
def wrapper(func):
|
10
|
-
self.routes.append(WebsocketRouteInternal(path, func))
|
11
|
-
return func
|
12
|
-
|
13
|
-
return wrapper
|
14
|
-
|
15
|
-
|
16
|
-
__all__ = ["WebsocketRoute", "WebSocketSession"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|