Encryptors 2.81.2__tar.gz → 2.81.4__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.81.2 → encryptors-2.81.4}/PKG-INFO +1 -1
- {encryptors-2.81.2 → encryptors-2.81.4}/setup.py +2 -2
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/PKG-INFO +1 -1
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/SOURCES.txt +24 -26
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Cli/__init__.py +7 -8
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/BaseRepository.py +1 -1
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/SqlExceptionTranslator.py +2 -4
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/StoredProcedureResultValidator.py +1 -4
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/Grpc.py +1 -1
- encryptors-2.81.4/src/Osdental/Decorators/SecureResolver.py +88 -0
- encryptors-2.81.4/src/Osdental/Encryptor/Aes.py +61 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/Bcrypt.py +4 -4
- encryptors-2.81.4/src/Osdental/Encryptor/Jwt.py +34 -0
- encryptors-2.81.4/src/Osdental/Encryptor/Rsa.py +47 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/Sha512.py +1 -2
- encryptors-2.81.4/src/Osdental/Enums/FileType.py +9 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Enums/GrahpqlOperation.py +2 -2
- encryptors-2.81.4/src/Osdental/Enums/GrpcStatusCode.py +25 -0
- encryptors-2.81.4/src/Osdental/Enums/StatusCode.py +40 -0
- encryptors-2.81.4/src/Osdental/Exception/ControlledException.py +49 -0
- encryptors-2.81.4/src/Osdental/Helpers/AuthorizationHelpers.py +53 -0
- encryptors-2.81.4/src/Osdental/Helpers/Logger.py +17 -0
- encryptors-2.81.4/src/Osdental/Helpers/RequestDecryption.py +33 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/ResponseDecryptor.py +1 -1
- encryptors-2.81.4/src/Osdental/Helpers/TokenValidationService.py +51 -0
- encryptors-2.81.4/src/Osdental/Http/HttpxClient.py +145 -0
- encryptors-2.81.4/src/Osdental/Http/__init__.py +69 -0
- encryptors-2.81.4/src/Osdental/Models/Response.py +11 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Models/Token.py +4 -5
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Models/base_graphql_context.py +3 -3
- encryptors-2.81.4/src/Osdental/Utils/FileMetaData.py +50 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/Mapper.py +13 -4
- encryptors-2.81.4/src/Osdental/Utils/PasswordGenerator.py +58 -0
- encryptors-2.81.2/src/Osdental/Decorators/SecureResolver.py +0 -119
- encryptors-2.81.2/src/Osdental/Encryptor/Aes.py +0 -85
- encryptors-2.81.2/src/Osdental/Encryptor/Jwt.py +0 -43
- encryptors-2.81.2/src/Osdental/Encryptor/Rsa.py +0 -58
- encryptors-2.81.2/src/Osdental/Exception/ControlledException.py +0 -159
- encryptors-2.81.2/src/Osdental/Helpers/extract_auth_token.py +0 -11
- encryptors-2.81.2/src/Osdental/Helpers/tenant_policy.py +0 -46
- encryptors-2.81.2/src/Osdental/Helpers/token_validation_service.py +0 -49
- encryptors-2.81.2/src/Osdental/Http/HttpxClient.py +0 -128
- encryptors-2.81.2/src/Osdental/Http/__init__.py +0 -30
- encryptors-2.81.2/src/Osdental/Models/Response.py +0 -13
- encryptors-2.81.2/src/Osdental/Shared/Enums/Code.py +0 -26
- encryptors-2.81.2/src/Osdental/Shared/Enums/Constant.py +0 -10
- encryptors-2.81.2/src/Osdental/Shared/Enums/FileType.py +0 -9
- encryptors-2.81.2/src/Osdental/Shared/Enums/Message.py +0 -40
- encryptors-2.81.2/src/Osdental/Shared/Logger.py +0 -17
- encryptors-2.81.2/src/Osdental/Shared/Utils/FileMetaData.py +0 -50
- encryptors-2.81.2/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -45
- encryptors-2.81.2/src/Osdental/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/README.md +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/setup.cfg +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/dependency_links.txt +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/entry_points.txt +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/requires.txt +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/top_level.txt +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Cache/Redis.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Cache/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/Connection.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/SqlTransientDetector.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/PublicResolver.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/Retry.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/Argon2.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/__init__.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Enums/Profile.py +0 -0
- {encryptors-2.81.2/src/Osdental/Exception → encryptors-2.81.4/src/Osdental/Enums}/__init__.py +0 -0
- {encryptors-2.81.2/src/Osdental/Helpers → encryptors-2.81.4/src/Osdental/Exception}/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/AzureClassifier.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/GrpcConnection.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/Resilience.py +0 -0
- {encryptors-2.81.2/src/Osdental/Models → encryptors-2.81.4/src/Osdental/Helpers}/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Constants.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Exceptions.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Messages.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Types.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/Kafka.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/RabbitMQ.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/__init__.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared/Enums → encryptors-2.81.4/src/Osdental/Models}/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Secrets/KeyVaultService.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Secrets/__init__.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Storage/S3Storage.py +0 -0
- {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Storage/__init__.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/CaseConverter.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/CodeGenerator.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/DataNormalizer.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/DataUtils.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/DateUtils.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/HashValidator.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/QueryGenerator.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/RsaUtils.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/TextProcessor.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/__init__.py +0 -0
- {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/__init__.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
setup(
|
|
4
4
|
name="Encryptors",
|
|
5
|
-
version="2.81.
|
|
5
|
+
version="2.81.4",
|
|
6
6
|
author="OSDental LLC",
|
|
7
7
|
author_email="support@osdental.ai",
|
|
8
8
|
description="End-to-end algorithm library",
|
|
@@ -28,16 +28,23 @@ src/Osdental/Encryptor/Jwt.py
|
|
|
28
28
|
src/Osdental/Encryptor/Rsa.py
|
|
29
29
|
src/Osdental/Encryptor/Sha512.py
|
|
30
30
|
src/Osdental/Encryptor/__init__.py
|
|
31
|
+
src/Osdental/Enums/FileType.py
|
|
32
|
+
src/Osdental/Enums/GrahpqlOperation.py
|
|
33
|
+
src/Osdental/Enums/GrpcStatusCode.py
|
|
34
|
+
src/Osdental/Enums/Profile.py
|
|
35
|
+
src/Osdental/Enums/StatusCode.py
|
|
36
|
+
src/Osdental/Enums/__init__.py
|
|
31
37
|
src/Osdental/Exception/ControlledException.py
|
|
32
38
|
src/Osdental/Exception/__init__.py
|
|
39
|
+
src/Osdental/Helpers/AuthorizationHelpers.py
|
|
33
40
|
src/Osdental/Helpers/AzureClassifier.py
|
|
34
41
|
src/Osdental/Helpers/GrpcConnection.py
|
|
42
|
+
src/Osdental/Helpers/Logger.py
|
|
43
|
+
src/Osdental/Helpers/RequestDecryption.py
|
|
35
44
|
src/Osdental/Helpers/Resilience.py
|
|
36
45
|
src/Osdental/Helpers/ResponseDecryptor.py
|
|
46
|
+
src/Osdental/Helpers/TokenValidationService.py
|
|
37
47
|
src/Osdental/Helpers/__init__.py
|
|
38
|
-
src/Osdental/Helpers/extract_auth_token.py
|
|
39
|
-
src/Osdental/Helpers/tenant_policy.py
|
|
40
|
-
src/Osdental/Helpers/token_validation_service.py
|
|
41
48
|
src/Osdental/Http/Constants.py
|
|
42
49
|
src/Osdental/Http/Exceptions.py
|
|
43
50
|
src/Osdental/Http/HttpxClient.py
|
|
@@ -54,28 +61,19 @@ src/Osdental/Models/__init__.py
|
|
|
54
61
|
src/Osdental/Models/base_graphql_context.py
|
|
55
62
|
src/Osdental/Secrets/KeyVaultService.py
|
|
56
63
|
src/Osdental/Secrets/__init__.py
|
|
57
|
-
src/Osdental/Shared/Logger.py
|
|
58
|
-
src/Osdental/Shared/__init__.py
|
|
59
|
-
src/Osdental/Shared/Enums/Code.py
|
|
60
|
-
src/Osdental/Shared/Enums/Constant.py
|
|
61
|
-
src/Osdental/Shared/Enums/FileType.py
|
|
62
|
-
src/Osdental/Shared/Enums/GrahpqlOperation.py
|
|
63
|
-
src/Osdental/Shared/Enums/Message.py
|
|
64
|
-
src/Osdental/Shared/Enums/Profile.py
|
|
65
|
-
src/Osdental/Shared/Enums/__init__.py
|
|
66
|
-
src/Osdental/Shared/Utils/CaseConverter.py
|
|
67
|
-
src/Osdental/Shared/Utils/CodeGenerator.py
|
|
68
|
-
src/Osdental/Shared/Utils/DataNormalizer.py
|
|
69
|
-
src/Osdental/Shared/Utils/DataUtils.py
|
|
70
|
-
src/Osdental/Shared/Utils/DateUtils.py
|
|
71
|
-
src/Osdental/Shared/Utils/FileMetaData.py
|
|
72
|
-
src/Osdental/Shared/Utils/HashValidator.py
|
|
73
|
-
src/Osdental/Shared/Utils/Mapper.py
|
|
74
|
-
src/Osdental/Shared/Utils/PasswordGenerator.py
|
|
75
|
-
src/Osdental/Shared/Utils/QueryGenerator.py
|
|
76
|
-
src/Osdental/Shared/Utils/RsaUtils.py
|
|
77
|
-
src/Osdental/Shared/Utils/TextProcessor.py
|
|
78
|
-
src/Osdental/Shared/Utils/__init__.py
|
|
79
64
|
src/Osdental/Storage/AzureBlobStorage.py
|
|
80
65
|
src/Osdental/Storage/S3Storage.py
|
|
81
|
-
src/Osdental/Storage/__init__.py
|
|
66
|
+
src/Osdental/Storage/__init__.py
|
|
67
|
+
src/Osdental/Utils/CaseConverter.py
|
|
68
|
+
src/Osdental/Utils/CodeGenerator.py
|
|
69
|
+
src/Osdental/Utils/DataNormalizer.py
|
|
70
|
+
src/Osdental/Utils/DataUtils.py
|
|
71
|
+
src/Osdental/Utils/DateUtils.py
|
|
72
|
+
src/Osdental/Utils/FileMetaData.py
|
|
73
|
+
src/Osdental/Utils/HashValidator.py
|
|
74
|
+
src/Osdental/Utils/Mapper.py
|
|
75
|
+
src/Osdental/Utils/PasswordGenerator.py
|
|
76
|
+
src/Osdental/Utils/QueryGenerator.py
|
|
77
|
+
src/Osdental/Utils/RsaUtils.py
|
|
78
|
+
src/Osdental/Utils/TextProcessor.py
|
|
79
|
+
src/Osdental/Utils/__init__.py
|
|
@@ -3,8 +3,7 @@ import sys
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import platform
|
|
5
5
|
import click
|
|
6
|
-
from Osdental.
|
|
7
|
-
from Osdental.Shared.Enums.Message import Message
|
|
6
|
+
from Osdental.Helpers.Logger import logger
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
@click.group()
|
|
@@ -20,7 +19,7 @@ def clean():
|
|
|
20
19
|
else:
|
|
21
20
|
subprocess.run("find . -name '__pycache__' -type d -exec rm -rf {} +", shell=True)
|
|
22
21
|
|
|
23
|
-
logger.info(
|
|
22
|
+
logger.info("All __pycache__ have been removed.")
|
|
24
23
|
|
|
25
24
|
@cli.command()
|
|
26
25
|
@click.argument('port')
|
|
@@ -29,7 +28,7 @@ def start(port: int):
|
|
|
29
28
|
try:
|
|
30
29
|
subprocess.run(['uvicorn', 'app:app', '--port', str(port), '--reload'], check=True)
|
|
31
30
|
except subprocess.CalledProcessError as e:
|
|
32
|
-
logger.error(f
|
|
31
|
+
logger.error(f"Error making the server accessible on the network. | {e}")
|
|
33
32
|
|
|
34
33
|
@cli.command()
|
|
35
34
|
@click.argument('port')
|
|
@@ -38,7 +37,7 @@ def serve(port: int):
|
|
|
38
37
|
try:
|
|
39
38
|
subprocess.run(['uvicorn', 'app:app', '--host', '0.0.0.0', '--port', str(port), '--reload'], check=True)
|
|
40
39
|
except subprocess.CalledProcessError as e:
|
|
41
|
-
logger.error(f
|
|
40
|
+
logger.error(f"Error making the server accessible on the network. | {e}")
|
|
42
41
|
|
|
43
42
|
@cli.command("clean-redis")
|
|
44
43
|
@click.argument('redis_env')
|
|
@@ -52,9 +51,9 @@ async def clean_redis(redis_env: str):
|
|
|
52
51
|
|
|
53
52
|
redis = RedisCacheAsync(redis_url=redis_url)
|
|
54
53
|
await redis.flush()
|
|
55
|
-
logger.info(
|
|
54
|
+
logger.info("Redis cleanup completed successfully.")
|
|
56
55
|
except Exception as e:
|
|
57
|
-
logger.error(f
|
|
56
|
+
logger.error(f"An error occurred while attempting to clean up Redis. | {e}")
|
|
58
57
|
|
|
59
58
|
@cli.command(name='proto-files')
|
|
60
59
|
@click.argument('name')
|
|
@@ -81,7 +80,7 @@ def proto_files(name: str):
|
|
|
81
80
|
]
|
|
82
81
|
|
|
83
82
|
subprocess.run(cmd, shell=False, check=True)
|
|
84
|
-
logger.info(
|
|
83
|
+
logger.info("Proto files have been successfully generated.")
|
|
85
84
|
|
|
86
85
|
@cli.command('run-server')
|
|
87
86
|
@click.option('--host', default="0.0.0.0", help='Host where to set up the server')
|
|
@@ -15,7 +15,7 @@ from Osdental.Database.SqlTransientDetector import SqlTransientDetector
|
|
|
15
15
|
from Osdental.Database.StoredProcedureResultValidator import (
|
|
16
16
|
StoredProcedureResultValidator,
|
|
17
17
|
)
|
|
18
|
-
from Osdental.
|
|
18
|
+
from Osdental.Utils.DataNormalizer import normalize
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class BaseRepository:
|
|
@@ -9,11 +9,9 @@ from sqlalchemy.exc import (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
from Osdental.Exception.ControlledException import DatabaseException
|
|
12
|
-
from Osdental.Shared.Enums.Message import Message
|
|
13
12
|
|
|
14
13
|
logger = logging.getLogger(__name__)
|
|
15
14
|
|
|
16
|
-
|
|
17
15
|
class SqlExceptionTranslator:
|
|
18
16
|
|
|
19
17
|
_ERRORS = {
|
|
@@ -39,7 +37,7 @@ class SqlExceptionTranslator:
|
|
|
39
37
|
),
|
|
40
38
|
|
|
41
39
|
DBAPIError: (
|
|
42
|
-
|
|
40
|
+
"Something went wrong while processing your request. Please try again later.",
|
|
43
41
|
"DB_ERROR",
|
|
44
42
|
),
|
|
45
43
|
}
|
|
@@ -64,7 +62,7 @@ class SqlExceptionTranslator:
|
|
|
64
62
|
)
|
|
65
63
|
|
|
66
64
|
return DatabaseException(
|
|
67
|
-
message=
|
|
65
|
+
message="Something went wrong while processing your request. Please try again later.",
|
|
68
66
|
error=detail,
|
|
69
67
|
status_code="UNEXPECTED_ERROR",
|
|
70
68
|
)
|
{encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/StoredProcedureResultValidator.py
RENAMED
|
@@ -2,7 +2,6 @@ from collections.abc import Iterable
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from Osdental.Exception.ControlledException import DatabaseException
|
|
5
|
-
from Osdental.Shared.Enums.Message import Message
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class StoredProcedureResultValidator:
|
|
@@ -44,7 +43,6 @@ class StoredProcedureResultValidator:
|
|
|
44
43
|
if status_code is None:
|
|
45
44
|
|
|
46
45
|
raise DatabaseException(
|
|
47
|
-
message="Status code missing.",
|
|
48
46
|
error="Stored Procedure did not return a status code.",
|
|
49
47
|
status_code="DB_STATUS_CODE_MISSING",
|
|
50
48
|
)
|
|
@@ -56,8 +54,7 @@ class StoredProcedureResultValidator:
|
|
|
56
54
|
if status_code not in success_codes:
|
|
57
55
|
|
|
58
56
|
raise DatabaseException(
|
|
59
|
-
|
|
60
|
-
error=status_message or Message.UNEXPECTED_ERROR_MSG,
|
|
57
|
+
error=status_message,
|
|
61
58
|
status_code=status_code,
|
|
62
59
|
)
|
|
63
60
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Callable
|
|
3
|
+
from Osdental.Models.Response import Response
|
|
4
|
+
from Osdental.Enums.Profile import Profile
|
|
5
|
+
from Osdental.Exception.ControlledException import (
|
|
6
|
+
OSDException, AccessDeniedException
|
|
7
|
+
)
|
|
8
|
+
from Osdental.Models.base_graphql_context import BaseGraphQLContext
|
|
9
|
+
from Osdental.Helpers.AuthorizationHelpers import tenant_context_resolver
|
|
10
|
+
from Osdental.Helpers.RequestDecryption import decrypt_payload
|
|
11
|
+
from Osdental.Secrets import ISecretProvider
|
|
12
|
+
from Osdental.Helpers.Logger import logger
|
|
13
|
+
|
|
14
|
+
_AES_KEY_USER = "AES-KEY-USER"
|
|
15
|
+
_AES_KEY_AUTH = "AES-KEY-AUTH"
|
|
16
|
+
_JWT_USER_KEY = "AUTH-JWT-USER-KEY"
|
|
17
|
+
|
|
18
|
+
def resolver(action=None):
|
|
19
|
+
|
|
20
|
+
def decorator(func: Callable):
|
|
21
|
+
|
|
22
|
+
@wraps(func)
|
|
23
|
+
async def wrapper(obj, info, **kwargs):
|
|
24
|
+
try:
|
|
25
|
+
context: BaseGraphQLContext = info.context
|
|
26
|
+
request = context.request
|
|
27
|
+
token_service = context.container.token_service
|
|
28
|
+
|
|
29
|
+
secret_service: ISecretProvider = context.container.secret_service
|
|
30
|
+
secrets = await secret_service.get_many(
|
|
31
|
+
[_AES_KEY_AUTH, _AES_KEY_USER, _JWT_USER_KEY]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
decrypted_payload = decrypt_payload(
|
|
35
|
+
encrypted_payload=kwargs.get("data"),
|
|
36
|
+
aes_auth_aes_key=secrets[_AES_KEY_AUTH]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if decrypted_payload is not None:
|
|
40
|
+
kwargs["data"] = decrypted_payload
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
original_token = await token_service.authenticate(
|
|
44
|
+
request.headers,
|
|
45
|
+
secrets[_JWT_USER_KEY],
|
|
46
|
+
secrets[_AES_KEY_USER],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
token = tenant_context_resolver(
|
|
50
|
+
token=original_token,
|
|
51
|
+
headers=request.headers,
|
|
52
|
+
decrypted_payload=decrypted_payload,
|
|
53
|
+
operation_type=info.operation.operation.value
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if action and Profile(token.abbreviation) not in action.allowed_roles:
|
|
57
|
+
raise AccessDeniedException(
|
|
58
|
+
error="User not allowed to perform this action."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
context.token = token
|
|
62
|
+
|
|
63
|
+
result = await func(obj, info, **kwargs)
|
|
64
|
+
|
|
65
|
+
if not isinstance(result, Response):
|
|
66
|
+
raise TypeError("Resolver must return a Response instance.")
|
|
67
|
+
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
except OSDException as e:
|
|
71
|
+
logger.warning(msg=str(e))
|
|
72
|
+
|
|
73
|
+
return Response(
|
|
74
|
+
status=e.status_code,
|
|
75
|
+
message=e.message,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.exception(msg=str(e))
|
|
80
|
+
|
|
81
|
+
return Response(
|
|
82
|
+
status="DB_ERROR_UNEXPECTED",
|
|
83
|
+
message="Could not process request.",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
return decorator
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import base64
|
|
4
|
+
from typing import Any
|
|
5
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
6
|
+
|
|
7
|
+
class AES:
|
|
8
|
+
|
|
9
|
+
IV_LENGTH = 32
|
|
10
|
+
TAG_LENGTH = 16
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def generate_key() -> str:
|
|
14
|
+
"""Generates a random 256-bit AES key and returns it Base64 encoded."""
|
|
15
|
+
key = os.urandom(32)
|
|
16
|
+
return base64.b64encode(key).decode("utf-8")
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def encrypt(
|
|
20
|
+
cls,
|
|
21
|
+
aes_key: str,
|
|
22
|
+
data: str | dict[str, Any] | list[dict[str, Any]]
|
|
23
|
+
) -> str:
|
|
24
|
+
|
|
25
|
+
if not isinstance(data, (dict, str, list)):
|
|
26
|
+
raise ValueError("Invalid data type: expected dict, str, or list.")
|
|
27
|
+
|
|
28
|
+
key = base64.b64decode(aes_key)
|
|
29
|
+
iv = os.urandom(cls.IV_LENGTH)
|
|
30
|
+
|
|
31
|
+
if isinstance(data, (dict, list)):
|
|
32
|
+
json_data = json.dumps(data)
|
|
33
|
+
else:
|
|
34
|
+
json_data = data
|
|
35
|
+
|
|
36
|
+
cipher = Cipher(algorithms.AES(key), modes.GCM(iv))
|
|
37
|
+
encryptor = cipher.encryptor()
|
|
38
|
+
ciphertext = encryptor.update(json_data.encode("utf-8")) + encryptor.finalize()
|
|
39
|
+
tag = encryptor.tag
|
|
40
|
+
encrypted_data = iv + ciphertext + tag
|
|
41
|
+
|
|
42
|
+
return base64.b64encode(encrypted_data).decode("utf-8")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def decrypt(cls, aes_key: str, encrypted_data: str):
|
|
47
|
+
|
|
48
|
+
key = base64.b64decode(aes_key)
|
|
49
|
+
encrypted_data = base64.b64decode(encrypted_data)
|
|
50
|
+
iv = encrypted_data[:cls.IV_LENGTH]
|
|
51
|
+
tag = encrypted_data[-cls.TAG_LENGTH:]
|
|
52
|
+
ciphertext = encrypted_data[cls.IV_LENGTH:-cls.TAG_LENGTH]
|
|
53
|
+
cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag))
|
|
54
|
+
decryptor = cipher.decryptor()
|
|
55
|
+
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
decrypted_data = json.loads(plaintext.decode("utf-8"))
|
|
59
|
+
return decrypted_data
|
|
60
|
+
except json.JSONDecodeError:
|
|
61
|
+
return plaintext.decode("utf-8")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import bcrypt
|
|
2
2
|
from Osdental.Exception.ControlledException import OSDException
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
|
|
5
5
|
class BcryptPasswordManager:
|
|
6
6
|
def __init__(self, rounds: int = 12):
|
|
@@ -21,8 +21,8 @@ class BcryptPasswordManager:
|
|
|
21
21
|
:return: string hash
|
|
22
22
|
"""
|
|
23
23
|
salt = bcrypt.gensalt(rounds=self.rounds)
|
|
24
|
-
hashed = bcrypt.hashpw(password.encode(
|
|
25
|
-
return hashed.decode(
|
|
24
|
+
hashed = bcrypt.hashpw(password.encode("utf-8"), salt)
|
|
25
|
+
return hashed.decode("utf-8")
|
|
26
26
|
|
|
27
27
|
def verify(self, hashed_password: str, plain_password: str) -> bool:
|
|
28
28
|
"""
|
|
@@ -32,7 +32,7 @@ class BcryptPasswordManager:
|
|
|
32
32
|
:return: True if matched, False if not
|
|
33
33
|
"""
|
|
34
34
|
return bcrypt.checkpw(
|
|
35
|
-
plain_password.encode(
|
|
35
|
+
plain_password.encode("utf-8"), hashed_password.encode("utf-8")
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
def needs_rehash(self, hashed_password: str) -> bool:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import jwt
|
|
2
|
+
from typing import Any
|
|
3
|
+
from cryptography.hazmat.primitives import serialization
|
|
4
|
+
from cryptography.hazmat.backends import default_backend
|
|
5
|
+
|
|
6
|
+
class JWT:
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def generate_token(
|
|
10
|
+
payload: dict[str, Any], jwt_secret_key: str, algorithm="HS256"
|
|
11
|
+
) -> str:
|
|
12
|
+
return jwt.encode(
|
|
13
|
+
payload=payload,
|
|
14
|
+
key=jwt_secret_key,
|
|
15
|
+
algorithm=algorithm,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def extract_payload(jwt_token: str, jwt_secret_key: str) -> dict[str, Any]:
|
|
21
|
+
return jwt.decode(
|
|
22
|
+
jwt=jwt_token,
|
|
23
|
+
key=jwt_secret_key,
|
|
24
|
+
algorithms=["HS256"],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def generate_private_key(private_rsa: str):
|
|
30
|
+
return serialization.load_pem_private_key(
|
|
31
|
+
private_rsa.encode(),
|
|
32
|
+
password=None,
|
|
33
|
+
backend=default_backend(),
|
|
34
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from base64 import b64decode, b64encode
|
|
3
|
+
from typing import Any
|
|
4
|
+
from cryptography.hazmat.primitives import serialization
|
|
5
|
+
from cryptography.hazmat.primitives.asymmetric import padding
|
|
6
|
+
from cryptography.hazmat.primitives import hashes
|
|
7
|
+
|
|
8
|
+
class RSAEncryptor:
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def encrypt(data: str | dict[str, Any], public_key_rsa: str) -> str:
|
|
12
|
+
|
|
13
|
+
if isinstance(data, dict):
|
|
14
|
+
data = json.dumps(data)
|
|
15
|
+
|
|
16
|
+
public_key = serialization.load_pem_public_key(public_key_rsa.encode("utf-8"))
|
|
17
|
+
data_bytes = data.encode("utf-8")
|
|
18
|
+
encrypted_bytes = public_key.encrypt(
|
|
19
|
+
data_bytes,
|
|
20
|
+
padding.OAEP(
|
|
21
|
+
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
22
|
+
algorithm=hashes.SHA256(),
|
|
23
|
+
label=None
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return b64encode(encrypted_bytes).decode("utf-8")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def decrypt(data: str, private_key_rsa: str) -> str:
|
|
32
|
+
|
|
33
|
+
encrypted_bytes = b64decode(data)
|
|
34
|
+
private_key = serialization.load_pem_private_key(
|
|
35
|
+
private_key_rsa.encode("utf-8"),
|
|
36
|
+
password=None
|
|
37
|
+
)
|
|
38
|
+
decrypted_bytes = private_key.decrypt(
|
|
39
|
+
encrypted_bytes,
|
|
40
|
+
padding.OAEP(
|
|
41
|
+
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
42
|
+
algorithm=hashes.SHA256(),
|
|
43
|
+
label=None
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return decrypted_bytes.decode("utf-8")
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import hashlib
|
|
2
|
-
from Osdental.Shared.Enums.Constant import Constant
|
|
3
2
|
|
|
4
3
|
class SHA512:
|
|
5
4
|
|
|
6
5
|
@staticmethod
|
|
7
6
|
def hash_password(password: str) -> str:
|
|
8
7
|
"""Generates the SHA-512 hash of a password."""
|
|
9
|
-
hash_object = hashlib.sha512(password.encode(
|
|
8
|
+
hash_object = hashlib.sha512(password.encode("utf-8"))
|
|
10
9
|
hashed_password = hash_object.hexdigest()
|
|
11
10
|
return hashed_password
|
|
12
11
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
class GrpcStatusCode(StrEnum):
|
|
4
|
+
# --- Éxito ---
|
|
5
|
+
OK = "OK" # Código 0: Operación exitosa.
|
|
6
|
+
|
|
7
|
+
# --- Errores del Cliente (4xx equivalentes) ---
|
|
8
|
+
CANCELLED = "CANCELLED" # Código 1: La operación fue cancelada por el cliente.
|
|
9
|
+
UNKNOWN = "UNKNOWN" # Código 2: Error desconocido (ej. una excepción no controlada).
|
|
10
|
+
INVALID_ARGUMENT = "INVALID_ARGUMENT" # Código 3: El cliente envió datos inválidos (parámetros mutados/vacíos).
|
|
11
|
+
DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED = " # Código 4: El tiempo de espera (Timeout) expiró antes de terminar.
|
|
12
|
+
NOT_FOUND = "NOT_FOUND" # Código 5: El recurso solicitado no fue encontrado.
|
|
13
|
+
ALREADY_EXISTS = "ALREADY_EXISTS" # Código 6: El recurso que se intentó crear ya existe.
|
|
14
|
+
PERMISSION_DENIED = "PERMISSION_DENIED" # Código 7: Elige este si el usuario está autenticado pero NO tiene permisos para este recurso.
|
|
15
|
+
RESOURCE_EXHAUSTED = "RESOURCE_EXHAUSTED" # Código 8: Se agotaron los recursos (Rate limit, cuotas de disco, etc.).
|
|
16
|
+
FAILED_PRECONDITION = "FAILED_PRECONDITION" # Código 9: El sistema no está en el estado requerido (ej. borrar un directorio que no está vacío).
|
|
17
|
+
ABORTED = "ABORTED" # Código 10: Operación abortada por un conflicto de concurrencia (ej. transacciones de BD).
|
|
18
|
+
OUT_OF_RANGE = "OUT_OF_RANGE" # Código 11: Operación pasada del rango válido (ej. un offset de paginación inválido).
|
|
19
|
+
UNAUTHENTICATED = "UNAUTHENTICATED" # Código 16: El usuario no está autenticado (Falta token, token expirado).
|
|
20
|
+
|
|
21
|
+
# --- Errores del Servidor (5xx equivalentes) ---
|
|
22
|
+
UNIMPLEMENTED = "UNIMPLEMENTED" # Código 12: El método o servicio no está implementado en el server.
|
|
23
|
+
INTERNAL = "INTERNAL" # Código 13: Errores graves internos del servidor (Bug en código, caída de BD principal).
|
|
24
|
+
UNAVAILABLE = "UNAVAILABLE" # Código 14: El microservicio o dependencia no está disponible (Servicio caído).
|
|
25
|
+
DATA_LOSS = "DATA_LOSS" # Código 15: Pérdida o corrupción de datos irrecuperable.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
class StatusCode(StrEnum):
|
|
4
|
+
|
|
5
|
+
# --- 2xx Éxito ---
|
|
6
|
+
OK = "SUCCESS_200"
|
|
7
|
+
CREATED = "SUCCESS_201"
|
|
8
|
+
ACCEPTED = "SUCCESS_202"
|
|
9
|
+
NO_CONTENT = "SUCCESS_204"
|
|
10
|
+
|
|
11
|
+
# --- 4xx Errores del Cliente ---
|
|
12
|
+
BAD_REQUEST = "ERROR_400"
|
|
13
|
+
UNAUTHORIZED = "ERROR_401"
|
|
14
|
+
FORBIDDEN = "ERROR_403"
|
|
15
|
+
NOT_FOUND = "ERROR_404"
|
|
16
|
+
CONFLICT = "ERROR_409"
|
|
17
|
+
UNPROCESSABLE_ENTITY = "ERROR_422"
|
|
18
|
+
|
|
19
|
+
# --- 5xx Errores del Servidor ---
|
|
20
|
+
INTERNAL_SERVER_ERROR = "ERROR_500"
|
|
21
|
+
BAD_GATEWAY = "ERROR_502"
|
|
22
|
+
SERVICE_UNAVAILABLE = "ERROR_503"
|
|
23
|
+
GATEWAY_TIMEOUT = "ERROR_504"
|
|
24
|
+
|
|
25
|
+
# --- Alertas de Negocio / Excepciones de Control ---
|
|
26
|
+
# NOTA: A diferencia de los 'ERROR_XXX' (que representan fallos técnicos o de integración),
|
|
27
|
+
# los 'WARNING_XXX' indican que el sistema operó correctamente pero el flujo fue detenido
|
|
28
|
+
# de forma controlada por reglas, validaciones o el estado del dominio.
|
|
29
|
+
|
|
30
|
+
# Formato inválido o datos de entrada que no cumplen con criterios lógicos
|
|
31
|
+
VALIDATION_WARNING = "WARNING_400"
|
|
32
|
+
|
|
33
|
+
# Recurso secundario, configuración o data externa requerida que no fue encontrada
|
|
34
|
+
DATA_NOT_FOUND_WARNING = "WARNING_404"
|
|
35
|
+
|
|
36
|
+
# Inviolabilidad del dominio (Ej: Usuario bloqueado, saldo insuficiente, cuenta inactiva)
|
|
37
|
+
BUSINESS_RULE_WARNING = "WARNING_422"
|
|
38
|
+
|
|
39
|
+
# Conflicto con el estado actual de la entidad (Ej: Intentar activar un registro ya activo)
|
|
40
|
+
STATE_CONFLICT_WARNING = "WARNING_409"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
class OSDException(Exception):
|
|
4
|
+
""" Base class for all custom exceptions. """
|
|
5
|
+
def __init__(
|
|
6
|
+
self,
|
|
7
|
+
message: str = None,
|
|
8
|
+
error: str = None,
|
|
9
|
+
status_code: Any = "DB_ERROR_CONTROLLED",
|
|
10
|
+
):
|
|
11
|
+
super().__init__(error)
|
|
12
|
+
self.message = message
|
|
13
|
+
self.error = error
|
|
14
|
+
self.status_code = status_code
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def __str__(self):
|
|
18
|
+
error_detail = self.error if self.error else "None"
|
|
19
|
+
return f"[{self.status_code}] {self.message} | {error_detail}"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class UnauthorizedException(OSDException):
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
message: str = "You are not authorized to access this portal.",
|
|
27
|
+
error: str = None,
|
|
28
|
+
status_code: str = None,
|
|
29
|
+
):
|
|
30
|
+
super().__init__(message=message, error=error, status_code=status_code)
|
|
31
|
+
|
|
32
|
+
class AccessDeniedException(OSDException):
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
message: str = "You do not have the necessary privileges for this operation.",
|
|
36
|
+
error: str = None,
|
|
37
|
+
status_code: str = None,
|
|
38
|
+
):
|
|
39
|
+
super().__init__(message=message, error=error, status_code=status_code)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DatabaseException(OSDException):
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
message: str = "Something went wrong while processing your request. Please try again later.",
|
|
46
|
+
error: str = None,
|
|
47
|
+
status_code: str = None,
|
|
48
|
+
):
|
|
49
|
+
super().__init__(message=message, error=error, status_code=status_code)
|