Encryptors 2.39__tar.gz → 2.41__tar.gz
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.
- {encryptors-2.39 → encryptors-2.41}/PKG-INFO +1 -20
- {encryptors-2.39 → encryptors-2.41}/setup.py +2 -2
- {encryptors-2.39 → encryptors-2.41}/src/Encryptors.egg-info/PKG-INFO +1 -20
- {encryptors-2.39 → encryptors-2.41}/src/Encryptors.egg-info/SOURCES.txt +3 -0
- encryptors-2.41/src/Osdental/Decorators/PublicResolver.py +3 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Decorators/SecureResolver.py +9 -4
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Encryptor/Jwt.py +3 -3
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Encryptor/Rsa.py +3 -3
- encryptors-2.41/src/Osdental/Graphql/Extensions/AuditExtension.py +189 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Helpers/AuditDispatcher.py +10 -1
- encryptors-2.41/src/Osdental/Helpers/ResponseDecryptor.py +15 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Models/Response.py +9 -1
- encryptors-2.41/src/Osdental/Shared/Utils/RsaUtils.py +30 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Storage/AzureBlobStorage.py +5 -1
- encryptors-2.41/src/Osdental/__init__.py +0 -0
- encryptors-2.39/README.md +0 -17
- encryptors-2.39/src/Osdental/Graphql/Extensions/AuditExtension.py +0 -120
- /encryptors-2.39/src/Osdental/Database/__init__.py → /encryptors-2.41/README.md +0 -0
- {encryptors-2.39 → encryptors-2.41}/setup.cfg +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Encryptors.egg-info/dependency_links.txt +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Encryptors.egg-info/entry_points.txt +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Encryptors.egg-info/requires.txt +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Encryptors.egg-info/top_level.txt +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Cli/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Database/BaseRepository.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Database/Connection.py +0 -0
- {encryptors-2.39/src/Osdental/Decorators → encryptors-2.41/src/Osdental/Database}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Decorators/Grpc.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Decorators/Retry.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Decorators/SqlDataNormalizer.py +0 -0
- {encryptors-2.39/src/Osdental/Encryptor → encryptors-2.41/src/Osdental/Decorators}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Encryptor/Aes.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Encryptor/Argon2.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Encryptor/Bcrypt.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Encryptor/Sha512.py +0 -0
- {encryptors-2.39/src/Osdental/Exception → encryptors-2.41/src/Osdental/Encryptor}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Exception/ControlledException.py +0 -0
- {encryptors-2.39/src/Osdental/Graphql/Extensions → encryptors-2.41/src/Osdental/Exception}/__init__.py +0 -0
- {encryptors-2.39/src/Osdental/Graphql/_Helpers → encryptors-2.41/src/Osdental/Graphql/Extensions}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Graphql/Models/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Graphql/_Exceptions/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Graphql/_Helpers/_AuditHelper.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Graphql/_Helpers/_TokenService.py +0 -0
- {encryptors-2.39/src/Osdental/Graphql → encryptors-2.41/src/Osdental/Graphql/_Helpers}/__init__.py +0 -0
- {encryptors-2.39/src/Osdental/Helpers → encryptors-2.41/src/Osdental/Graphql}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Helpers/KeyVaultService.py +0 -0
- {encryptors-2.39/src/Osdental/Http → encryptors-2.41/src/Osdental/Helpers}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Http/APIClient.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Http/_Exceptions.py +0 -0
- {encryptors-2.39/src/Osdental/Models → encryptors-2.41/src/Osdental/Http}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Messaging/Kafka.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Messaging/RabbitMQ.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Messaging/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Models/AuditConfig.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Models/Token.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Models/_Audit.py +0 -0
- {encryptors-2.39/src/Osdental/RedisCache → encryptors-2.41/src/Osdental/Models}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/RedisCache/Redis.py +0 -0
- {encryptors-2.39/src/Osdental/Rest/Context → encryptors-2.41/src/Osdental/RedisCache}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Rest/Context/RequestContext.py +0 -0
- {encryptors-2.39/src/Osdental/Rest/Middlewares → encryptors-2.41/src/Osdental/Rest/Context}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +0 -0
- {encryptors-2.39/src/Osdental/Rest → encryptors-2.41/src/Osdental/Rest/Middlewares}/__init__.py +0 -0
- {encryptors-2.39/src/Osdental/Shared/Enums → encryptors-2.41/src/Osdental/Rest}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Enums/Code.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Enums/Constant.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Enums/FileType.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Enums/GrahpqlOperation.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Enums/Message.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Enums/Profile.py +0 -0
- {encryptors-2.39/src/Osdental/Shared/Utils → encryptors-2.41/src/Osdental/Shared/Enums}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Logger.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/CaseConverter.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/CodeGenerator.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/DataNormalizer.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/DataUtils.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/DateUtils.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/FileMetaData.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/HashValidator.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/Mapper.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/QueryGenerator.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Shared/Utils/TextProcessor.py +0 -0
- {encryptors-2.39/src/Osdental/Shared → encryptors-2.41/src/Osdental/Shared/Utils}/__init__.py +0 -0
- {encryptors-2.39/src/Osdental → encryptors-2.41/src/Osdental/Shared}/__init__.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Storage/S3Storage.py +0 -0
- {encryptors-2.39 → encryptors-2.41}/src/Osdental/Storage/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.41
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -59,26 +59,7 @@ Requires-Dist: gunicorn==23.0.0
|
|
|
59
59
|
Dynamic: author
|
|
60
60
|
Dynamic: author-email
|
|
61
61
|
Dynamic: classifier
|
|
62
|
-
Dynamic: description
|
|
63
62
|
Dynamic: description-content-type
|
|
64
63
|
Dynamic: requires-dist
|
|
65
64
|
Dynamic: requires-python
|
|
66
65
|
Dynamic: summary
|
|
67
|
-
|
|
68
|
-
# osdental-library
|
|
69
|
-
|
|
70
|
-
`osdental-library` is a versatile and easy-to-use library for handling common tasks related to **encryption**, **hashing**, and **JWT token management**. Ideal for projects that require a secure and efficient approach to handling sensitive data.
|
|
71
|
-
|
|
72
|
-
## Features
|
|
73
|
-
|
|
74
|
-
- Generation and validation of **hashes** using modern algorithms.
|
|
75
|
-
- Encryption and decryption of data using secure keys.
|
|
76
|
-
- Creation and verification of **JWT tokens** for authentication and authorization.
|
|
77
|
-
- Implementations that are easy to integrate into any Python project.
|
|
78
|
-
|
|
79
|
-
## Installation
|
|
80
|
-
|
|
81
|
-
You can easily install `osdental-library` using `pip`:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
pip install osdental-library
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
|
-
|
|
2
|
+
# ANDERSON ESTO YA SE SUBIO Y ESTA ESTABLE, AUN TE QUEDA PENDIENTE LA AUDITORIA CON RSA Y AES DE ACCESSTOKEN
|
|
3
3
|
setup(
|
|
4
4
|
name="Encryptors",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2.41",
|
|
6
6
|
author="OSDental LLC",
|
|
7
7
|
author_email="support@osdental.ai",
|
|
8
8
|
description="End-to-end algorithm library",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.41
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -59,26 +59,7 @@ Requires-Dist: gunicorn==23.0.0
|
|
|
59
59
|
Dynamic: author
|
|
60
60
|
Dynamic: author-email
|
|
61
61
|
Dynamic: classifier
|
|
62
|
-
Dynamic: description
|
|
63
62
|
Dynamic: description-content-type
|
|
64
63
|
Dynamic: requires-dist
|
|
65
64
|
Dynamic: requires-python
|
|
66
65
|
Dynamic: summary
|
|
67
|
-
|
|
68
|
-
# osdental-library
|
|
69
|
-
|
|
70
|
-
`osdental-library` is a versatile and easy-to-use library for handling common tasks related to **encryption**, **hashing**, and **JWT token management**. Ideal for projects that require a secure and efficient approach to handling sensitive data.
|
|
71
|
-
|
|
72
|
-
## Features
|
|
73
|
-
|
|
74
|
-
- Generation and validation of **hashes** using modern algorithms.
|
|
75
|
-
- Encryption and decryption of data using secure keys.
|
|
76
|
-
- Creation and verification of **JWT tokens** for authentication and authorization.
|
|
77
|
-
- Implementations that are easy to integrate into any Python project.
|
|
78
|
-
|
|
79
|
-
## Installation
|
|
80
|
-
|
|
81
|
-
You can easily install `osdental-library` using `pip`:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
pip install osdental-library
|
|
@@ -12,6 +12,7 @@ src/Osdental/Database/BaseRepository.py
|
|
|
12
12
|
src/Osdental/Database/Connection.py
|
|
13
13
|
src/Osdental/Database/__init__.py
|
|
14
14
|
src/Osdental/Decorators/Grpc.py
|
|
15
|
+
src/Osdental/Decorators/PublicResolver.py
|
|
15
16
|
src/Osdental/Decorators/Retry.py
|
|
16
17
|
src/Osdental/Decorators/SecureResolver.py
|
|
17
18
|
src/Osdental/Decorators/SqlDataNormalizer.py
|
|
@@ -37,6 +38,7 @@ src/Osdental/Graphql/_Helpers/_TokenService.py
|
|
|
37
38
|
src/Osdental/Graphql/_Helpers/__init__.py
|
|
38
39
|
src/Osdental/Helpers/AuditDispatcher.py
|
|
39
40
|
src/Osdental/Helpers/KeyVaultService.py
|
|
41
|
+
src/Osdental/Helpers/ResponseDecryptor.py
|
|
40
42
|
src/Osdental/Helpers/__init__.py
|
|
41
43
|
src/Osdental/Http/APIClient.py
|
|
42
44
|
src/Osdental/Http/_Exceptions.py
|
|
@@ -76,6 +78,7 @@ src/Osdental/Shared/Utils/HashValidator.py
|
|
|
76
78
|
src/Osdental/Shared/Utils/Mapper.py
|
|
77
79
|
src/Osdental/Shared/Utils/PasswordGenerator.py
|
|
78
80
|
src/Osdental/Shared/Utils/QueryGenerator.py
|
|
81
|
+
src/Osdental/Shared/Utils/RsaUtils.py
|
|
79
82
|
src/Osdental/Shared/Utils/TextProcessor.py
|
|
80
83
|
src/Osdental/Shared/Utils/__init__.py
|
|
81
84
|
src/Osdental/Storage/AzureBlobStorage.py
|
|
@@ -8,12 +8,13 @@ from Osdental.Shared.Enums.Profile import Profile
|
|
|
8
8
|
from Osdental.Exception.ControlledException import OSDException, AccessDeniedException
|
|
9
9
|
from Osdental.Encryptor.Aes import AES
|
|
10
10
|
from Osdental.Graphql.Models import BaseGraphQLContext
|
|
11
|
+
from Osdental.Shared.Logger import logger
|
|
11
12
|
|
|
12
13
|
def secure_resolver(action = None):
|
|
13
14
|
def decorator(func: Callable):
|
|
14
15
|
|
|
15
16
|
signature = inspect.signature(func)
|
|
16
|
-
|
|
17
|
+
params = signature.parameters
|
|
17
18
|
|
|
18
19
|
@wraps(func)
|
|
19
20
|
async def wrapper(obj: Any, context: BaseGraphQLContext, **kwargs: Dict[str, Any]):
|
|
@@ -26,19 +27,23 @@ def secure_resolver(action = None):
|
|
|
26
27
|
if Profile(token.abbreviation) not in action.allowed_roles:
|
|
27
28
|
raise AccessDeniedException(error="The user's profile is not allowed to perform this action.")
|
|
28
29
|
|
|
29
|
-
if
|
|
30
|
+
if "token" in params:
|
|
31
|
+
kwargs["token"] = token
|
|
32
|
+
|
|
33
|
+
if "data" in params:
|
|
30
34
|
kwargs["data"] = payload
|
|
31
35
|
|
|
32
|
-
result: Response = await func(obj,
|
|
36
|
+
result: Response = await func(obj, **kwargs)
|
|
33
37
|
|
|
34
38
|
context.audit_plain_response = copy.deepcopy(result)
|
|
35
39
|
|
|
36
|
-
if result.data is not None:
|
|
40
|
+
if result.data is not None and result.encryption_type == "AES" and result.key is None:
|
|
37
41
|
result.data = AES.encrypt(aes_auth, result.data)
|
|
38
42
|
|
|
39
43
|
return result.send()
|
|
40
44
|
|
|
41
45
|
except (Exception, OSDException) as e:
|
|
46
|
+
logger.exception(f"An error has occurred: {str(e)}")
|
|
42
47
|
result = Response(
|
|
43
48
|
status=getattr(e, "status_code", "DB_ERROR_UNEXPECTED"),
|
|
44
49
|
message=getattr(e, "message", "Could not process request."),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import jwt
|
|
2
|
-
from typing import Dict
|
|
2
|
+
from typing import Dict, Any
|
|
3
3
|
from cryptography.hazmat.primitives import serialization
|
|
4
4
|
from cryptography.hazmat.backends import default_backend
|
|
5
5
|
from Osdental.Exception.ControlledException import JWTokenException
|
|
@@ -11,7 +11,7 @@ class JWT:
|
|
|
11
11
|
|
|
12
12
|
@staticmethod
|
|
13
13
|
def generate_token(
|
|
14
|
-
payload: Dict[str,
|
|
14
|
+
payload: Dict[str, Any], jwt_secret_key: str, algorithm="HS256"
|
|
15
15
|
) -> str:
|
|
16
16
|
try:
|
|
17
17
|
token = jwt.encode(payload, jwt_secret_key, algorithm=algorithm)
|
|
@@ -22,7 +22,7 @@ class JWT:
|
|
|
22
22
|
raise JWTokenException(message=Message.UNEXPECTED_ERROR_MSG, error=str(e))
|
|
23
23
|
|
|
24
24
|
@staticmethod
|
|
25
|
-
def extract_payload(jwt_token: str, jwt_secret_key: str) -> Dict[str,
|
|
25
|
+
def extract_payload(jwt_token: str, jwt_secret_key: str) -> Dict[str, Any]:
|
|
26
26
|
try:
|
|
27
27
|
payload = jwt.decode(jwt_token, jwt_secret_key, algorithms=["HS256"])
|
|
28
28
|
return payload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from base64 import b64decode, b64encode
|
|
3
|
-
from typing import Dict
|
|
3
|
+
from typing import Dict, Any
|
|
4
4
|
from cryptography.hazmat.primitives import serialization
|
|
5
5
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
6
6
|
from cryptography.hazmat.primitives import hashes
|
|
@@ -12,7 +12,7 @@ from Osdental.Shared.Enums.Constant import Constant
|
|
|
12
12
|
class RSAEncryptor:
|
|
13
13
|
|
|
14
14
|
@staticmethod
|
|
15
|
-
def encrypt(data:str | Dict[str,
|
|
15
|
+
def encrypt(data: str | Dict[str, Any], public_key_rsa: str) -> str:
|
|
16
16
|
try:
|
|
17
17
|
if isinstance(data, dict):
|
|
18
18
|
data = json.dumps(data)
|
|
@@ -36,7 +36,7 @@ class RSAEncryptor:
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
@staticmethod
|
|
39
|
-
def decrypt(data:str, private_key_rsa:str, silent:bool = False) -> str:
|
|
39
|
+
def decrypt(data: str, private_key_rsa: str, silent: bool = False) -> str:
|
|
40
40
|
try:
|
|
41
41
|
encrypted_bytes = b64decode(data)
|
|
42
42
|
private_key = serialization.load_pem_private_key(private_key_rsa.encode(Constant.DEFAULT_ENCODING), password=None)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from graphql.pyutils import is_awaitable
|
|
3
|
+
from ariadne.types import Extension
|
|
4
|
+
from Osdental.Shared.Logger import logger
|
|
5
|
+
from Osdental.Encryptor.Aes import AES
|
|
6
|
+
from Osdental.Graphql._Helpers._TenantPolicy import TenantPolicy
|
|
7
|
+
from Osdental.Rest.Context.RequestContext import (
|
|
8
|
+
current_context,
|
|
9
|
+
RequestContext
|
|
10
|
+
)
|
|
11
|
+
from Osdental.Graphql.Models import BaseGraphQLContext
|
|
12
|
+
from Osdental.Shared.Enums.Constant import Constant
|
|
13
|
+
|
|
14
|
+
class AuditExtension(Extension):
|
|
15
|
+
|
|
16
|
+
def request_started(self, context):
|
|
17
|
+
request = context.request
|
|
18
|
+
self._ctx_token = current_context.set(
|
|
19
|
+
RequestContext(
|
|
20
|
+
request=request,
|
|
21
|
+
request_id=request.headers.get("x-request-id"),
|
|
22
|
+
user=None
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
self.errors = None
|
|
27
|
+
self.request_payload = None
|
|
28
|
+
self.result = None
|
|
29
|
+
|
|
30
|
+
def _should_skip(self, body) -> bool:
|
|
31
|
+
query = body.get("query", "") or ""
|
|
32
|
+
return "__schema" in query or "__type(" in query
|
|
33
|
+
|
|
34
|
+
async def resolve(self, next_, root, info, **kwargs):
|
|
35
|
+
|
|
36
|
+
context: BaseGraphQLContext = info.context
|
|
37
|
+
request = context.request
|
|
38
|
+
|
|
39
|
+
# cache body
|
|
40
|
+
if not hasattr(context, "_cached_body"):
|
|
41
|
+
context._cached_body = await request.json()
|
|
42
|
+
|
|
43
|
+
body = context._cached_body
|
|
44
|
+
|
|
45
|
+
# skip introspection
|
|
46
|
+
if self._should_skip(body):
|
|
47
|
+
result = next_(root, info, **kwargs)
|
|
48
|
+
if is_awaitable(result):
|
|
49
|
+
result = await result
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
is_root = root is None
|
|
53
|
+
|
|
54
|
+
# SOLO ROOT
|
|
55
|
+
if is_root:
|
|
56
|
+
|
|
57
|
+
# identificar resolver público
|
|
58
|
+
resolver_fn = getattr(next_, "__wrapped__", next_)
|
|
59
|
+
is_public = getattr(resolver_fn, "_is_public", False)
|
|
60
|
+
|
|
61
|
+
# inicializar auth UNA SOLA VEZ
|
|
62
|
+
if not hasattr(context, "_auth_initialized"):
|
|
63
|
+
context._auth_initialized = True
|
|
64
|
+
|
|
65
|
+
headers = request.headers
|
|
66
|
+
container = context.container
|
|
67
|
+
token_service = container.token_service
|
|
68
|
+
|
|
69
|
+
aes_auth = request.app.state.aes_auth
|
|
70
|
+
aes_user = request.app.state.aes_user
|
|
71
|
+
|
|
72
|
+
if not aes_auth or not aes_user:
|
|
73
|
+
aes_auth = None
|
|
74
|
+
aes_user = None
|
|
75
|
+
|
|
76
|
+
original_token = None
|
|
77
|
+
if not is_public and headers.get("authorization"):
|
|
78
|
+
try:
|
|
79
|
+
original_token = await token_service.authenticate(headers, aes_user)
|
|
80
|
+
except Exception:
|
|
81
|
+
original_token = None
|
|
82
|
+
|
|
83
|
+
context._original_token = original_token
|
|
84
|
+
context.token = original_token
|
|
85
|
+
context.aes_auth = aes_auth
|
|
86
|
+
|
|
87
|
+
# VALIDACIÓN SOLO ROOT
|
|
88
|
+
original_token = getattr(context, "_original_token", None)
|
|
89
|
+
|
|
90
|
+
if not is_public and not original_token:
|
|
91
|
+
raise ValueError("Authorization required")
|
|
92
|
+
|
|
93
|
+
# payload
|
|
94
|
+
variables = body.get("variables") or {}
|
|
95
|
+
|
|
96
|
+
encrypted_payload = (
|
|
97
|
+
kwargs.get("data")
|
|
98
|
+
or variables.get("data")
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
decrypted_payload = None
|
|
102
|
+
|
|
103
|
+
if encrypted_payload is not None:
|
|
104
|
+
if isinstance(encrypted_payload, dict):
|
|
105
|
+
decrypted_payload = encrypted_payload
|
|
106
|
+
|
|
107
|
+
elif isinstance(encrypted_payload, str):
|
|
108
|
+
try:
|
|
109
|
+
decrypted_payload = json.loads(encrypted_payload)
|
|
110
|
+
except Exception:
|
|
111
|
+
if context.aes_auth:
|
|
112
|
+
try:
|
|
113
|
+
decrypted_payload = AES.decrypt(context.aes_auth, encrypted_payload)
|
|
114
|
+
try:
|
|
115
|
+
decrypted_payload = json.loads(decrypted_payload)
|
|
116
|
+
except Exception:
|
|
117
|
+
pass
|
|
118
|
+
except Exception:
|
|
119
|
+
decrypted_payload = None
|
|
120
|
+
|
|
121
|
+
if decrypted_payload is not None:
|
|
122
|
+
context.decrypted_payload = decrypted_payload
|
|
123
|
+
|
|
124
|
+
if not is_public:
|
|
125
|
+
token = TenantPolicy.resolve(
|
|
126
|
+
token=context._original_token,
|
|
127
|
+
headers=request.headers,
|
|
128
|
+
decrypted_payload=decrypted_payload,
|
|
129
|
+
operation_type=info.operation.operation.value
|
|
130
|
+
)
|
|
131
|
+
context.token = token
|
|
132
|
+
|
|
133
|
+
# auditoría request payload
|
|
134
|
+
if not self.request_payload:
|
|
135
|
+
token = context.token
|
|
136
|
+
|
|
137
|
+
self.request = request
|
|
138
|
+
self.request_payload = {
|
|
139
|
+
"operation_type": info.operation.operation.value,
|
|
140
|
+
"operation_name": body.get("operationName", "UnknownOperation"),
|
|
141
|
+
"query": body.get("query"),
|
|
142
|
+
"variables": decrypted_payload,
|
|
143
|
+
"user": token.user_full_name if token else "Public"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# SIEMPRE ejecutar resolver (root + fields)
|
|
147
|
+
result = next_(root, info, **kwargs)
|
|
148
|
+
|
|
149
|
+
if is_awaitable(result):
|
|
150
|
+
result = await result
|
|
151
|
+
|
|
152
|
+
# SOLO ROOT captura resultado final
|
|
153
|
+
if is_root and self.result is None:
|
|
154
|
+
self.result = result
|
|
155
|
+
|
|
156
|
+
if not getattr(context, "audit_plain_response", None):
|
|
157
|
+
context.audit_plain_response = result
|
|
158
|
+
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
def has_errors(self, errors, context):
|
|
162
|
+
self.errors = errors
|
|
163
|
+
|
|
164
|
+
def request_finished(self, context):
|
|
165
|
+
|
|
166
|
+
if not self.request_payload:
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
query = self.request_payload.get("query", "")
|
|
170
|
+
|
|
171
|
+
if "__schema" in query or "__type" in query:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
final_result = context.audit_plain_response or self.result
|
|
175
|
+
|
|
176
|
+
if final_result is None:
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
dispatcher = context.request.app.state.audit_dispatcher
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
dispatcher.dispatch(
|
|
183
|
+
request=self.request,
|
|
184
|
+
request_payload=self.request_payload,
|
|
185
|
+
result=final_result,
|
|
186
|
+
audit_type=Constant.MESSAGE_LOG_INTERNAL
|
|
187
|
+
)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger(f"[AUDIT ERROR]: {str(e)}")
|
|
@@ -4,6 +4,7 @@ from fastapi import Request
|
|
|
4
4
|
from Osdental.Models.AuditConfig import AuditConfig
|
|
5
5
|
from Osdental.Messaging import IMessageQueue
|
|
6
6
|
from Osdental.Graphql._Helpers._AuditHelper import AuditHelper
|
|
7
|
+
from Osdental.Helpers.ResponseDecryptor import decryptor_data, VALID_TYPES
|
|
7
8
|
from Osdental.Models._Audit import Audit
|
|
8
9
|
from Osdental.Models.Response import Response
|
|
9
10
|
from Osdental.Shared.Enums.Constant import Constant
|
|
@@ -74,7 +75,7 @@ class AuditDispatcher:
|
|
|
74
75
|
async def _process(self, payload: Dict[str, Any]):
|
|
75
76
|
request = payload["request"]
|
|
76
77
|
request_payload = payload["request_payload"]
|
|
77
|
-
result = payload["result"]
|
|
78
|
+
result: Response = payload["result"]
|
|
78
79
|
audit_type = payload["audit_type"]
|
|
79
80
|
|
|
80
81
|
operation_name = request_payload.get("operation_name")
|
|
@@ -96,6 +97,11 @@ class AuditDispatcher:
|
|
|
96
97
|
message = result.message
|
|
97
98
|
data = result.data
|
|
98
99
|
error = result.error if result.error else message
|
|
100
|
+
|
|
101
|
+
# Obtencion de campos adicionales cuando es otro tipo de encriptacion o clave
|
|
102
|
+
encryption_type = result.encryption_type
|
|
103
|
+
key = result.key
|
|
104
|
+
|
|
99
105
|
|
|
100
106
|
if audit_type == Constant.MESSAGE_LOG_INTERNAL:
|
|
101
107
|
|
|
@@ -121,6 +127,9 @@ class AuditDispatcher:
|
|
|
121
127
|
audit_message = request_audit_payload | payload
|
|
122
128
|
|
|
123
129
|
else:
|
|
130
|
+
if encryption_type in VALID_TYPES and key is not None:
|
|
131
|
+
data = decryptor_data(encryption_type, key, data)
|
|
132
|
+
|
|
124
133
|
payload = AuditHelper.build_final_payload(
|
|
125
134
|
_type="RESPONSE",
|
|
126
135
|
status_code=status_code,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
from Osdental.Encryptor.Aes import AES
|
|
3
|
+
from Osdental.Encryptor.Rsa import RSAEncryptor
|
|
4
|
+
|
|
5
|
+
VALID_TYPES = {"RSA", "AES"}
|
|
6
|
+
|
|
7
|
+
def decryptor_data(encryption_type: str, key: str, data: Dict[str, Any] | str) -> Any:
|
|
8
|
+
|
|
9
|
+
match encryption_type:
|
|
10
|
+
|
|
11
|
+
case "AES":
|
|
12
|
+
return AES.decrypt(key, data)
|
|
13
|
+
|
|
14
|
+
case "RSA":
|
|
15
|
+
return RSAEncryptor.decrypt(data, key)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field, asdict
|
|
2
|
-
from typing import Optional, Any
|
|
2
|
+
from typing import Optional, Any, Literal
|
|
3
3
|
from Osdental.Shared.Enums.Code import Code
|
|
4
4
|
from Osdental.Shared.Enums.Message import Message
|
|
5
5
|
|
|
@@ -9,6 +9,8 @@ class Response:
|
|
|
9
9
|
message: str = field(default=Message.PROCESS_SUCCESS_MSG)
|
|
10
10
|
data: Optional[Any] = None
|
|
11
11
|
error: Optional[str] = None
|
|
12
|
+
encryption_type: Literal["AES", "RSA", "NORMAL"] = "AES"
|
|
13
|
+
key: Optional[str] = None
|
|
12
14
|
|
|
13
15
|
def send(self):
|
|
14
16
|
response = asdict(self)
|
|
@@ -16,4 +18,10 @@ class Response:
|
|
|
16
18
|
if response["error"] is None:
|
|
17
19
|
response.pop("error")
|
|
18
20
|
|
|
21
|
+
if response["encryption_type"]:
|
|
22
|
+
response.pop("encryption_type")
|
|
23
|
+
|
|
24
|
+
if response["key"]:
|
|
25
|
+
response.pop("key")
|
|
26
|
+
|
|
19
27
|
return response
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
def normalize_rsa_keys(key_str: str, is_private: bool = True) -> str:
|
|
3
|
+
"""
|
|
4
|
+
Normaliza una llave RSA (privada o pública) para que sea compatible con cryptography.
|
|
5
|
+
Si is_private=True, normaliza la llave privada; si False, normaliza la llave pública.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Determinar encabezados correctos
|
|
9
|
+
if is_private:
|
|
10
|
+
header = "-----BEGIN RSA PRIVATE KEY-----"
|
|
11
|
+
footer = "-----END RSA PRIVATE KEY-----"
|
|
12
|
+
invalid_texts = ["-----BEGINRSAPRIVATEKEY-----", "-----ENDRSAPRIVATEKEY-----",
|
|
13
|
+
"-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----"]
|
|
14
|
+
else:
|
|
15
|
+
header = "-----BEGIN PUBLIC KEY-----"
|
|
16
|
+
footer = "-----END PUBLIC KEY-----"
|
|
17
|
+
invalid_texts = ["-----BEGINPUBLICKEY-----", "-----ENDPUBLICKEY-----",
|
|
18
|
+
"-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----"]
|
|
19
|
+
|
|
20
|
+
# Limpiar encabezados inválidos y espacios
|
|
21
|
+
for text in invalid_texts:
|
|
22
|
+
key_str = key_str.replace(text, "")
|
|
23
|
+
key_str = key_str.replace("\r", "").replace(" ", "").replace("\n", "")
|
|
24
|
+
|
|
25
|
+
# Dividir en líneas de 64 caracteres
|
|
26
|
+
lines = [key_str[i:i+64] for i in range(0, len(key_str), 64)]
|
|
27
|
+
|
|
28
|
+
# Reconstruir PEM válido
|
|
29
|
+
pem = f"{header}\n" + "\n".join(lines) + f"\n{footer}\n"
|
|
30
|
+
return pem
|
|
File without changes
|
encryptors-2.39/README.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# osdental-library
|
|
2
|
-
|
|
3
|
-
`osdental-library` is a versatile and easy-to-use library for handling common tasks related to **encryption**, **hashing**, and **JWT token management**. Ideal for projects that require a secure and efficient approach to handling sensitive data.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Generation and validation of **hashes** using modern algorithms.
|
|
8
|
-
- Encryption and decryption of data using secure keys.
|
|
9
|
-
- Creation and verification of **JWT tokens** for authentication and authorization.
|
|
10
|
-
- Implementations that are easy to integrate into any Python project.
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
You can easily install `osdental-library` using `pip`:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
pip install osdental-library
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from graphql.pyutils import is_awaitable
|
|
3
|
-
from ariadne.types import Extension
|
|
4
|
-
from Osdental.Encryptor.Aes import AES
|
|
5
|
-
from Osdental.Graphql._Helpers._TenantPolicy import TenantPolicy
|
|
6
|
-
from Osdental.Rest.Context.RequestContext import (
|
|
7
|
-
current_context,
|
|
8
|
-
RequestContext
|
|
9
|
-
)
|
|
10
|
-
from Osdental.Graphql.Models import BaseGraphQLContext
|
|
11
|
-
from Osdental.Graphql._Exceptions import AESKeyNotFound
|
|
12
|
-
from Osdental.Shared.Enums.Constant import Constant
|
|
13
|
-
|
|
14
|
-
class AuditExtension(Extension):
|
|
15
|
-
|
|
16
|
-
def request_started(self, context):
|
|
17
|
-
request = context.request
|
|
18
|
-
self._ctx_token = current_context.set(
|
|
19
|
-
RequestContext(
|
|
20
|
-
request=request,
|
|
21
|
-
request_id=request.headers.get("x-request-id"),
|
|
22
|
-
user=None
|
|
23
|
-
)
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
self.errors = None
|
|
27
|
-
self.request_payload = None
|
|
28
|
-
self.result = None
|
|
29
|
-
|
|
30
|
-
async def resolve(self, next_, root, info, **kwargs):
|
|
31
|
-
|
|
32
|
-
if not self.request_payload:
|
|
33
|
-
context: BaseGraphQLContext = info.context
|
|
34
|
-
|
|
35
|
-
request = context.request
|
|
36
|
-
self.request = request
|
|
37
|
-
|
|
38
|
-
body = await request.json()
|
|
39
|
-
headers = request.headers
|
|
40
|
-
|
|
41
|
-
container = context.container
|
|
42
|
-
token_service = container.token_service
|
|
43
|
-
|
|
44
|
-
aes_auth = request.app.state.aes_auth
|
|
45
|
-
aes_user = request.app.state.aes_user
|
|
46
|
-
|
|
47
|
-
if not aes_auth:
|
|
48
|
-
raise AESKeyNotFound("AES user key is missing.", "aes_auth")
|
|
49
|
-
|
|
50
|
-
if not aes_user:
|
|
51
|
-
raise AESKeyNotFound("AES authorization key is missing.", "aes_user")
|
|
52
|
-
|
|
53
|
-
original_token = await token_service.authenticate(headers, aes_user)
|
|
54
|
-
|
|
55
|
-
variables = body.get("variables") or {}
|
|
56
|
-
encrypted_payload = variables.get("data")
|
|
57
|
-
|
|
58
|
-
decrypted_payload = None
|
|
59
|
-
|
|
60
|
-
if encrypted_payload:
|
|
61
|
-
try:
|
|
62
|
-
decrypted_payload = AES.decrypt(aes_auth, encrypted_payload)
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
decrypted_payload = json.loads(decrypted_payload)
|
|
66
|
-
except Exception:
|
|
67
|
-
pass
|
|
68
|
-
|
|
69
|
-
except Exception:
|
|
70
|
-
decrypted_payload = None
|
|
71
|
-
|
|
72
|
-
token = TenantPolicy.resolve(
|
|
73
|
-
token=original_token,
|
|
74
|
-
headers=request.headers,
|
|
75
|
-
decrypted_payload=decrypted_payload,
|
|
76
|
-
operation_type=info.operation.operation.value
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
self.request_payload = {
|
|
80
|
-
"operation_type": info.operation.operation.value,
|
|
81
|
-
"operation_name": body.get("operationName", "UnknownOperation"),
|
|
82
|
-
"query": body.get("query"),
|
|
83
|
-
"variables": decrypted_payload,
|
|
84
|
-
"user": token.user_full_name
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
context.decrypted_payload = decrypted_payload
|
|
88
|
-
context.token = token
|
|
89
|
-
context.aes_auth = aes_auth
|
|
90
|
-
|
|
91
|
-
result = next_(root, info, **kwargs)
|
|
92
|
-
|
|
93
|
-
if is_awaitable(result):
|
|
94
|
-
result = await result
|
|
95
|
-
|
|
96
|
-
if root is None:
|
|
97
|
-
self.result = result
|
|
98
|
-
|
|
99
|
-
return result
|
|
100
|
-
|
|
101
|
-
def has_errors(self, errors, context):
|
|
102
|
-
self.errors = errors
|
|
103
|
-
|
|
104
|
-
def request_finished(self, context):
|
|
105
|
-
if not self.request_payload: return
|
|
106
|
-
|
|
107
|
-
query = self.request_payload.get("query", "")
|
|
108
|
-
|
|
109
|
-
if "__schema" in query or "__type" in query: return
|
|
110
|
-
|
|
111
|
-
if not hasattr(context, "audit_plain_response"): return
|
|
112
|
-
|
|
113
|
-
dispatcher = context.request.app.state.audit_dispatcher
|
|
114
|
-
|
|
115
|
-
dispatcher.dispatch(
|
|
116
|
-
request=self.request,
|
|
117
|
-
request_payload=self.request_payload,
|
|
118
|
-
result=context.audit_plain_response,
|
|
119
|
-
audit_type=Constant.MESSAGE_LOG_INTERNAL
|
|
120
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Decorators → encryptors-2.41/src/Osdental/Database}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Encryptor → encryptors-2.41/src/Osdental/Decorators}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Exception → encryptors-2.41/src/Osdental/Encryptor}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Graphql → encryptors-2.41/src/Osdental/Graphql/_Helpers}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Rest/Context → encryptors-2.41/src/Osdental/RedisCache}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39 → encryptors-2.41}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py
RENAMED
|
File without changes
|
{encryptors-2.39/src/Osdental/Rest → encryptors-2.41/src/Osdental/Rest/Middlewares}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Shared/Utils → encryptors-2.41/src/Osdental/Shared/Enums}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.39/src/Osdental/Shared → encryptors-2.41/src/Osdental/Shared/Utils}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|