Encryptors 2.49__tar.gz → 2.51__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.49 → encryptors-2.51}/PKG-INFO +2 -1
- {encryptors-2.49 → encryptors-2.51}/setup.py +3 -2
- {encryptors-2.49 → encryptors-2.51}/src/Encryptors.egg-info/PKG-INFO +2 -1
- {encryptors-2.49 → encryptors-2.51}/src/Encryptors.egg-info/SOURCES.txt +5 -1
- {encryptors-2.49 → encryptors-2.51}/src/Encryptors.egg-info/requires.txt +1 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Cache/Redis.py +86 -28
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Database/Connection.py +11 -0
- encryptors-2.51/src/Osdental/Decorators/SecureResolver.py +134 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Exception/ControlledException.py +18 -18
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/AzureClassifier.py +4 -0
- encryptors-2.51/src/Osdental/Helpers/JwtAuthTokenService.py +147 -0
- encryptors-2.51/src/Osdental/Helpers/JwtTokenHelper.py +171 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/Resilience.py +96 -29
- encryptors-2.51/src/Osdental/Helpers/_AuthTokenProcessor.py +99 -0
- encryptors-2.51/src/Osdental/Helpers/_Ports.py +47 -0
- encryptors-2.51/src/Osdental/Messaging/AzureServiceBus.py +146 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/ApiResponse.py +2 -2
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/Response.py +2 -2
- encryptors-2.51/src/Osdental/Models/TokenClaims.py +46 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Enums/Profile.py +2 -1
- encryptors-2.51/src/Osdental/Shared/Enums/StatusCode.py +40 -0
- encryptors-2.51/src/Osdental/Shared/Utils/RsaUtils.py +56 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Storage/AzureBlobStorage.py +19 -16
- encryptors-2.49/src/Osdental/Decorators/SecureResolver.py +0 -59
- encryptors-2.49/src/Osdental/Helpers/_Ports.py +0 -11
- encryptors-2.49/src/Osdental/Messaging/AzureServiceBus.py +0 -70
- encryptors-2.49/src/Osdental/Shared/Enums/Code.py +0 -26
- encryptors-2.49/src/Osdental/Shared/Utils/RsaUtils.py +0 -30
- {encryptors-2.49 → encryptors-2.51}/README.md +0 -0
- {encryptors-2.49 → encryptors-2.51}/setup.cfg +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Encryptors.egg-info/dependency_links.txt +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Encryptors.egg-info/entry_points.txt +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Encryptors.egg-info/top_level.txt +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Cache/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Cli/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Database/BaseRepository.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Database/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Decorators/Grpc.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Decorators/PublicResolver.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Decorators/Retry.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Decorators/SqlDataNormalizer.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Decorators/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/Aes.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/Argon2.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/Bcrypt.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/Jwt.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/Rsa.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/Sha512.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Encryptor/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Exception/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/Extensions/AuditExtension.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/Extensions/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/Models/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/_Exceptions/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/_Helpers/_AuditHelper.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/_Helpers/_TokenService.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/_Helpers/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Graphql/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/AuditDispatcher.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/GrpcConnection.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/KeyVaultService.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/ResponseDecryptor.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/WebsocketClient.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Helpers/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Http/APIClient.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Http/_Helpers.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Http/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Messaging/Kafka.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Messaging/RabbitMQ.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Messaging/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/AuditConfig.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/Notification.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/Token.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/_Audit.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Models/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Rest/Context/RequestContext.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Rest/Context/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Rest/Middlewares/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Rest/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Enums/Constant.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Enums/FileType.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Enums/GrahpqlOperation.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Enums/Message.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Enums/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Logger.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/CaseConverter.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/CodeGenerator.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/DataNormalizer.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/DataUtils.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/DateUtils.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/FileMetaData.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/HashValidator.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/Mapper.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/QueryGenerator.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/TextProcessor.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/Utils/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Shared/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Storage/S3Storage.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/Storage/__init__.py +0 -0
- {encryptors-2.49 → encryptors-2.51}/src/Osdental/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.51
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -57,6 +57,7 @@ Requires-Dist: bcrypt==4.3.0
|
|
|
57
57
|
Requires-Dist: azure-monitor-opentelemetry==1.8.1
|
|
58
58
|
Requires-Dist: uvicorn==0.37.0
|
|
59
59
|
Requires-Dist: gunicorn==23.0.0
|
|
60
|
+
Requires-Dist: jwcrypto==1.5.7
|
|
60
61
|
Dynamic: author
|
|
61
62
|
Dynamic: author-email
|
|
62
63
|
Dynamic: classifier
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
# ANDERSON REVISAR EL CACHE LOCAL DEL KEYVAULT PARA VALIDAR SI FUNCIONA
|
|
3
3
|
setup(
|
|
4
4
|
name="Encryptors",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2.51",
|
|
6
6
|
author="OSDental LLC",
|
|
7
7
|
author_email="support@osdental.ai",
|
|
8
8
|
description="End-to-end algorithm library",
|
|
@@ -64,7 +64,8 @@ setup(
|
|
|
64
64
|
"bcrypt==4.3.0",
|
|
65
65
|
"azure-monitor-opentelemetry==1.8.1",
|
|
66
66
|
"uvicorn==0.37.0",
|
|
67
|
-
"gunicorn==23.0.0"
|
|
67
|
+
"gunicorn==23.0.0",
|
|
68
|
+
"jwcrypto==1.5.7",
|
|
68
69
|
],
|
|
69
70
|
entry_points={
|
|
70
71
|
'console_scripts': [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.51
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -57,6 +57,7 @@ Requires-Dist: bcrypt==4.3.0
|
|
|
57
57
|
Requires-Dist: azure-monitor-opentelemetry==1.8.1
|
|
58
58
|
Requires-Dist: uvicorn==0.37.0
|
|
59
59
|
Requires-Dist: gunicorn==23.0.0
|
|
60
|
+
Requires-Dist: jwcrypto==1.5.7
|
|
60
61
|
Dynamic: author
|
|
61
62
|
Dynamic: author-email
|
|
62
63
|
Dynamic: classifier
|
|
@@ -41,10 +41,13 @@ src/Osdental/Graphql/_Helpers/__init__.py
|
|
|
41
41
|
src/Osdental/Helpers/AuditDispatcher.py
|
|
42
42
|
src/Osdental/Helpers/AzureClassifier.py
|
|
43
43
|
src/Osdental/Helpers/GrpcConnection.py
|
|
44
|
+
src/Osdental/Helpers/JwtAuthTokenService.py
|
|
45
|
+
src/Osdental/Helpers/JwtTokenHelper.py
|
|
44
46
|
src/Osdental/Helpers/KeyVaultService.py
|
|
45
47
|
src/Osdental/Helpers/Resilience.py
|
|
46
48
|
src/Osdental/Helpers/ResponseDecryptor.py
|
|
47
49
|
src/Osdental/Helpers/WebsocketClient.py
|
|
50
|
+
src/Osdental/Helpers/_AuthTokenProcessor.py
|
|
48
51
|
src/Osdental/Helpers/_Ports.py
|
|
49
52
|
src/Osdental/Helpers/__init__.py
|
|
50
53
|
src/Osdental/Http/APIClient.py
|
|
@@ -59,6 +62,7 @@ src/Osdental/Models/AuditConfig.py
|
|
|
59
62
|
src/Osdental/Models/Notification.py
|
|
60
63
|
src/Osdental/Models/Response.py
|
|
61
64
|
src/Osdental/Models/Token.py
|
|
65
|
+
src/Osdental/Models/TokenClaims.py
|
|
62
66
|
src/Osdental/Models/_Audit.py
|
|
63
67
|
src/Osdental/Models/__init__.py
|
|
64
68
|
src/Osdental/Rest/__init__.py
|
|
@@ -68,12 +72,12 @@ src/Osdental/Rest/Middlewares/RequestContextMiddleware.py
|
|
|
68
72
|
src/Osdental/Rest/Middlewares/__init__.py
|
|
69
73
|
src/Osdental/Shared/Logger.py
|
|
70
74
|
src/Osdental/Shared/__init__.py
|
|
71
|
-
src/Osdental/Shared/Enums/Code.py
|
|
72
75
|
src/Osdental/Shared/Enums/Constant.py
|
|
73
76
|
src/Osdental/Shared/Enums/FileType.py
|
|
74
77
|
src/Osdental/Shared/Enums/GrahpqlOperation.py
|
|
75
78
|
src/Osdental/Shared/Enums/Message.py
|
|
76
79
|
src/Osdental/Shared/Enums/Profile.py
|
|
80
|
+
src/Osdental/Shared/Enums/StatusCode.py
|
|
77
81
|
src/Osdental/Shared/Enums/__init__.py
|
|
78
82
|
src/Osdental/Shared/Utils/CaseConverter.py
|
|
79
83
|
src/Osdental/Shared/Utils/CodeGenerator.py
|
|
@@ -18,12 +18,6 @@ class RedisCacheAsync(ICacheService):
|
|
|
18
18
|
_instances: Dict[str, "RedisCacheAsync"] = {}
|
|
19
19
|
_lock = asyncio.Lock()
|
|
20
20
|
|
|
21
|
-
_policy = AzureResiliencePolicy(
|
|
22
|
-
"redis-cache",
|
|
23
|
-
max_attempts=3,
|
|
24
|
-
base_delay=0.5,
|
|
25
|
-
failure_threshold=5,
|
|
26
|
-
)
|
|
27
21
|
|
|
28
22
|
def __new__(cls, redis_host: str, redis_port: int = 6380):
|
|
29
23
|
|
|
@@ -48,7 +42,15 @@ class RedisCacheAsync(ICacheService):
|
|
|
48
42
|
|
|
49
43
|
self.client = None
|
|
50
44
|
|
|
45
|
+
self._policy = AzureResiliencePolicy(
|
|
46
|
+
"redis-cache",
|
|
47
|
+
max_attempts=3,
|
|
48
|
+
base_delay=0.5,
|
|
49
|
+
failure_threshold=5,
|
|
50
|
+
)
|
|
51
|
+
|
|
51
52
|
self._initialized = True
|
|
53
|
+
self._reconnect_lock = asyncio.Lock()
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
async def _call(self, func, *args, **kwargs):
|
|
@@ -68,8 +70,24 @@ class RedisCacheAsync(ICacheService):
|
|
|
68
70
|
try:
|
|
69
71
|
return await self._policy.execute(_inner)
|
|
70
72
|
except AzureTransientError as exc:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
try:
|
|
74
|
+
await self._reconnect()
|
|
75
|
+
|
|
76
|
+
except Exception as reconnect_exc:
|
|
77
|
+
logger.error(
|
|
78
|
+
"redis.reconnect.failed error=%s",
|
|
79
|
+
reconnect_exc
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
logger.error(
|
|
83
|
+
"redis.exhausted error=%s",
|
|
84
|
+
exc
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
raise RedisException(
|
|
88
|
+
message=Message.UNEXPECTED_ERROR_MSG,
|
|
89
|
+
error=str(exc)
|
|
90
|
+
)
|
|
73
91
|
except Exception as exc:
|
|
74
92
|
raise RedisException(message=Message.UNEXPECTED_ERROR_MSG, error=str(exc))
|
|
75
93
|
|
|
@@ -100,7 +118,7 @@ class RedisCacheAsync(ICacheService):
|
|
|
100
118
|
retry_on_timeout=True,
|
|
101
119
|
socket_connect_timeout=5,
|
|
102
120
|
socket_timeout=5,
|
|
103
|
-
max_connections=
|
|
121
|
+
max_connections=100,
|
|
104
122
|
socket_keepalive=True
|
|
105
123
|
)
|
|
106
124
|
|
|
@@ -135,11 +153,10 @@ class RedisCacheAsync(ICacheService):
|
|
|
135
153
|
):
|
|
136
154
|
|
|
137
155
|
await self._call(
|
|
138
|
-
self.client.set
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)
|
|
156
|
+
self.client.set,
|
|
157
|
+
key,
|
|
158
|
+
json.dumps(value),
|
|
159
|
+
ex=ttl
|
|
143
160
|
)
|
|
144
161
|
|
|
145
162
|
|
|
@@ -151,11 +168,10 @@ class RedisCacheAsync(ICacheService):
|
|
|
151
168
|
):
|
|
152
169
|
|
|
153
170
|
await self._call(
|
|
154
|
-
self.client.set
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
171
|
+
self.client.set,
|
|
172
|
+
key,
|
|
173
|
+
value,
|
|
174
|
+
ex=ttl
|
|
159
175
|
)
|
|
160
176
|
|
|
161
177
|
async def get_dict(
|
|
@@ -164,10 +180,19 @@ class RedisCacheAsync(ICacheService):
|
|
|
164
180
|
) -> Optional[Dict[str, Any]]:
|
|
165
181
|
|
|
166
182
|
value = await self._call(
|
|
167
|
-
self.client.get
|
|
183
|
+
self.client.get,
|
|
184
|
+
key
|
|
168
185
|
)
|
|
169
186
|
|
|
170
|
-
|
|
187
|
+
try:
|
|
188
|
+
return json.loads(value) if value else None
|
|
189
|
+
|
|
190
|
+
except json.JSONDecodeError:
|
|
191
|
+
logger.error(
|
|
192
|
+
"redis.invalid_json key=%s",
|
|
193
|
+
key
|
|
194
|
+
)
|
|
195
|
+
return None
|
|
171
196
|
|
|
172
197
|
|
|
173
198
|
async def get_str(
|
|
@@ -176,14 +201,16 @@ class RedisCacheAsync(ICacheService):
|
|
|
176
201
|
) -> Optional[str]:
|
|
177
202
|
|
|
178
203
|
return await self._call(
|
|
179
|
-
self.client.get
|
|
204
|
+
self.client.get,
|
|
205
|
+
key
|
|
180
206
|
)
|
|
181
207
|
|
|
182
208
|
|
|
183
209
|
async def delete(self, key: str) -> bool:
|
|
184
210
|
|
|
185
211
|
result = await self._call(
|
|
186
|
-
self.client.delete
|
|
212
|
+
self.client.delete,
|
|
213
|
+
key
|
|
187
214
|
)
|
|
188
215
|
|
|
189
216
|
return result > 0
|
|
@@ -192,7 +219,8 @@ class RedisCacheAsync(ICacheService):
|
|
|
192
219
|
async def exists(self, key: str) -> bool:
|
|
193
220
|
|
|
194
221
|
result = await self._call(
|
|
195
|
-
self.client.exists
|
|
222
|
+
self.client.exists,
|
|
223
|
+
key
|
|
196
224
|
)
|
|
197
225
|
|
|
198
226
|
return result > 0
|
|
@@ -201,14 +229,14 @@ class RedisCacheAsync(ICacheService):
|
|
|
201
229
|
async def flush(self):
|
|
202
230
|
|
|
203
231
|
await self._call(
|
|
204
|
-
self.client.flushdb
|
|
232
|
+
self.client.flushdb
|
|
205
233
|
)
|
|
206
234
|
|
|
207
235
|
|
|
208
236
|
async def flush_all(self):
|
|
209
237
|
|
|
210
238
|
await self._call(
|
|
211
|
-
self.client.flushall
|
|
239
|
+
self.client.flushall
|
|
212
240
|
)
|
|
213
241
|
|
|
214
242
|
|
|
@@ -218,7 +246,8 @@ class RedisCacheAsync(ICacheService):
|
|
|
218
246
|
) -> List[Optional[Any]]:
|
|
219
247
|
|
|
220
248
|
values = await self._call(
|
|
221
|
-
self.client.mget
|
|
249
|
+
self.client.mget,
|
|
250
|
+
keys
|
|
222
251
|
)
|
|
223
252
|
|
|
224
253
|
return [
|
|
@@ -236,7 +265,10 @@ class RedisCacheAsync(ICacheService):
|
|
|
236
265
|
match=f"{prefix}*"
|
|
237
266
|
):
|
|
238
267
|
|
|
239
|
-
await self._call(
|
|
268
|
+
await self._call(
|
|
269
|
+
self.client.delete,
|
|
270
|
+
key
|
|
271
|
+
)
|
|
240
272
|
|
|
241
273
|
|
|
242
274
|
async def _scan_keys(self, match: str = "*"):
|
|
@@ -246,6 +278,32 @@ class RedisCacheAsync(ICacheService):
|
|
|
246
278
|
):
|
|
247
279
|
yield key
|
|
248
280
|
|
|
281
|
+
|
|
282
|
+
async def _reconnect(self):
|
|
283
|
+
|
|
284
|
+
async with self._reconnect_lock:
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
|
|
288
|
+
if self.client:
|
|
289
|
+
await self.client.ping()
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
except Exception:
|
|
293
|
+
pass
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
|
|
297
|
+
if self.client:
|
|
298
|
+
await self.client.aclose()
|
|
299
|
+
|
|
300
|
+
except Exception:
|
|
301
|
+
pass
|
|
302
|
+
|
|
303
|
+
self.client = None
|
|
304
|
+
|
|
305
|
+
await self.connect()
|
|
306
|
+
|
|
249
307
|
async def close(self):
|
|
250
308
|
|
|
251
309
|
try:
|
|
@@ -74,6 +74,17 @@ class Connection:
|
|
|
74
74
|
self.initialized = True
|
|
75
75
|
logger.info("db.connection_ready url=%s", self._safe_url(db_url))
|
|
76
76
|
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _safe_url(db_url: str) -> str:
|
|
79
|
+
"""Enmascara contraseñas del connection string para logging seguro."""
|
|
80
|
+
import re
|
|
81
|
+
return re.sub(
|
|
82
|
+
r"(password|pwd)=[^;]+",
|
|
83
|
+
r"\1=***",
|
|
84
|
+
db_url,
|
|
85
|
+
flags=re.IGNORECASE
|
|
86
|
+
)
|
|
87
|
+
|
|
77
88
|
def _ensure_connection_resiliency(self, db_url: str) -> str:
|
|
78
89
|
"""
|
|
79
90
|
Agrega parámetros resilientes por defecto para Azure SQL
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Callable
|
|
3
|
+
from graphql import GraphQLResolveInfo
|
|
4
|
+
from Osdental.Models.Response import Response
|
|
5
|
+
from Osdental.Shared.Enums.Profile import Profile
|
|
6
|
+
from Osdental.Exception.ControlledException import (
|
|
7
|
+
OSDException, AccessDeniedException
|
|
8
|
+
)
|
|
9
|
+
from Osdental.Graphql.Models import BaseGraphQLContext
|
|
10
|
+
from Osdental.Shared.Logger import logger
|
|
11
|
+
from Osdental.Helpers._Ports import IAuthTokenService
|
|
12
|
+
from Osdental.Helpers._AuthTokenProcessor import (
|
|
13
|
+
extract_bearer_token, build_auth_token, decrypt_and_parse_payload
|
|
14
|
+
)
|
|
15
|
+
from Osdental.Models.TokenClaims import UserTokenClaims
|
|
16
|
+
from Osdental.Shared.Enums.StatusCode import StatusCode
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def __test(dispatcher, request, request_payload, result, decrypted_key):
|
|
20
|
+
dispatcher.dispatch(
|
|
21
|
+
request=request,
|
|
22
|
+
request_payload=request_payload,
|
|
23
|
+
result=result,
|
|
24
|
+
metadata={
|
|
25
|
+
"decrypted_key": decrypted_key
|
|
26
|
+
},
|
|
27
|
+
audit_type="MESSAGE_LOG_INTERNAL"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def resolver(public: bool = False, action=None):
|
|
31
|
+
|
|
32
|
+
def decorator(func: Callable):
|
|
33
|
+
func._is_public = public
|
|
34
|
+
|
|
35
|
+
@wraps(func)
|
|
36
|
+
async def wrapper(obj, info: GraphQLResolveInfo, **kwargs):
|
|
37
|
+
try:
|
|
38
|
+
context: BaseGraphQLContext = info.context
|
|
39
|
+
request = context.request
|
|
40
|
+
|
|
41
|
+
# ── 1. AUTENTICACIÓN ──────────────────────────────
|
|
42
|
+
if not public:
|
|
43
|
+
|
|
44
|
+
headers = request.headers
|
|
45
|
+
container = context.container
|
|
46
|
+
token_service: IAuthTokenService = container.auth_token_service
|
|
47
|
+
user_token = extract_bearer_token(headers)
|
|
48
|
+
|
|
49
|
+
claims: UserTokenClaims = token_service.validate_internal_access_token(
|
|
50
|
+
user_token
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# ── 2. DECRYPT DEL PAYLOAD ────────────────────────
|
|
54
|
+
body = await request.json()
|
|
55
|
+
variables = body.get("variables") or {}
|
|
56
|
+
encrypted_payload = kwargs.get("data") or variables.get("data")
|
|
57
|
+
decrypted_payload = None
|
|
58
|
+
|
|
59
|
+
if not public and encrypted_payload is not None:
|
|
60
|
+
decrypted_payload = decrypt_and_parse_payload(
|
|
61
|
+
aes_key_auth=claims.aes_key_auth,
|
|
62
|
+
encrypted_payload=encrypted_payload
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
kwargs["data"] = decrypted_payload
|
|
66
|
+
|
|
67
|
+
# ── 3. TENANT POLICY ──────────────────────────────
|
|
68
|
+
if not public:
|
|
69
|
+
|
|
70
|
+
token = build_auth_token(
|
|
71
|
+
claims=claims,
|
|
72
|
+
headers=request.headers,
|
|
73
|
+
decrypted_payload=decrypted_payload,
|
|
74
|
+
operation_type=info.operation.operation.value
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# ── 4. CONTROL DE ACCESO POR ROL ─────────────────
|
|
78
|
+
if not public and action:
|
|
79
|
+
if Profile(token.abbreviation) not in action.allowed_roles:
|
|
80
|
+
raise AccessDeniedException(
|
|
81
|
+
status_code=StatusCode.BUSINESS_RULE_WARNING,
|
|
82
|
+
error="User not allowed to perform this action"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# ── 5. EJECUTAR RESOLVER ──────────────────────────
|
|
86
|
+
result = await func(obj, info, **kwargs)
|
|
87
|
+
|
|
88
|
+
if not isinstance(result, Response):
|
|
89
|
+
raise TypeError("Resolver must return a Response instance")
|
|
90
|
+
|
|
91
|
+
dispatcher = request.app.state.audit_dispatcher
|
|
92
|
+
|
|
93
|
+
request_payload = {
|
|
94
|
+
"operation_type": info.operation.operation.value,
|
|
95
|
+
"operation_name": body.get("operationName", "UnknownOperation"),
|
|
96
|
+
"query": body.get("query"),
|
|
97
|
+
"variables": decrypted_payload,
|
|
98
|
+
"user": (
|
|
99
|
+
token.user_full_name
|
|
100
|
+
if token
|
|
101
|
+
else "Public"
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if isinstance(result, Response):
|
|
106
|
+
decrypted_key = result.key or claims.aes_key_auth
|
|
107
|
+
|
|
108
|
+
__test(
|
|
109
|
+
dispatcher=dispatcher,
|
|
110
|
+
request_payload=request_payload,
|
|
111
|
+
request=request,
|
|
112
|
+
decrypted_key=decrypted_key,
|
|
113
|
+
result=result
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
except OSDException as e:
|
|
119
|
+
logger.warning(f"Business error: {str(e)}")
|
|
120
|
+
return Response(
|
|
121
|
+
status=e.status_code,
|
|
122
|
+
message=e.message,
|
|
123
|
+
error=e.error
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
logger.exception(f"Unexpected error: {str(e)}")
|
|
127
|
+
return Response(
|
|
128
|
+
status=StatusCode.INTERNAL_SERVER_ERROR,
|
|
129
|
+
message="Could not process request.",
|
|
130
|
+
error=str(e)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return wrapper
|
|
134
|
+
return decorator
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Any
|
|
2
|
-
from Osdental.Shared.Enums.
|
|
2
|
+
from Osdental.Shared.Enums.StatusCode import StatusCode
|
|
3
3
|
from Osdental.Shared.Enums.Message import Message
|
|
4
4
|
|
|
5
5
|
class OSDException(Exception):
|
|
@@ -8,7 +8,7 @@ class OSDException(Exception):
|
|
|
8
8
|
self,
|
|
9
9
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
10
10
|
error: str = None,
|
|
11
|
-
status_code: Any =
|
|
11
|
+
status_code: Any = StatusCode.INTERNAL_SERVER_ERROR,
|
|
12
12
|
):
|
|
13
13
|
super().__init__(error)
|
|
14
14
|
self.message = message
|
|
@@ -25,7 +25,7 @@ class UnauthorizedException(OSDException):
|
|
|
25
25
|
self,
|
|
26
26
|
message: str = Message.PORTAL_ACCESS_RESTRICTED_MSG,
|
|
27
27
|
error: str = None,
|
|
28
|
-
status_code: str =
|
|
28
|
+
status_code: str = StatusCode.UNAUTHORIZED,
|
|
29
29
|
):
|
|
30
30
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
31
31
|
|
|
@@ -34,7 +34,7 @@ class AccessDeniedException(OSDException):
|
|
|
34
34
|
self,
|
|
35
35
|
message: str = Message.ACCESS_DENIED_MSG,
|
|
36
36
|
error: str = None,
|
|
37
|
-
status_code: str =
|
|
37
|
+
status_code: str = StatusCode.BUSINESS_RULE_WARNING,
|
|
38
38
|
):
|
|
39
39
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
40
40
|
|
|
@@ -43,7 +43,7 @@ class RequestDataException(OSDException):
|
|
|
43
43
|
self,
|
|
44
44
|
message: str = Message.INVALID_REQUEST_PARAMS_MSG,
|
|
45
45
|
error: str = None,
|
|
46
|
-
status_code: str =
|
|
46
|
+
status_code: str = StatusCode.VALIDATION_WARNING,
|
|
47
47
|
):
|
|
48
48
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
49
49
|
|
|
@@ -52,7 +52,7 @@ class DatabaseConnectionException(OSDException):
|
|
|
52
52
|
self,
|
|
53
53
|
message: str = Message.INVALID_FORMAT_MSG,
|
|
54
54
|
error: str = None,
|
|
55
|
-
status_code: str =
|
|
55
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
56
56
|
):
|
|
57
57
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
58
58
|
|
|
@@ -61,7 +61,7 @@ class DatabaseException(OSDException):
|
|
|
61
61
|
self,
|
|
62
62
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
63
63
|
error: str = None,
|
|
64
|
-
status_code: str =
|
|
64
|
+
status_code: str = StatusCode.DATA_NOT_FOUND_WARNING,
|
|
65
65
|
):
|
|
66
66
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
67
67
|
|
|
@@ -70,7 +70,7 @@ class RSAEncryptException(OSDException):
|
|
|
70
70
|
self,
|
|
71
71
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
72
72
|
error: str = None,
|
|
73
|
-
status_code: str =
|
|
73
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
74
74
|
):
|
|
75
75
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
76
76
|
|
|
@@ -79,7 +79,7 @@ class AESEncryptException(OSDException):
|
|
|
79
79
|
self,
|
|
80
80
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
81
81
|
error: str = None,
|
|
82
|
-
status_code: str =
|
|
82
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
83
83
|
):
|
|
84
84
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
85
85
|
|
|
@@ -88,7 +88,7 @@ class JWTokenException(OSDException):
|
|
|
88
88
|
self,
|
|
89
89
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
90
90
|
error: str = None,
|
|
91
|
-
status_code: str =
|
|
91
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
92
92
|
):
|
|
93
93
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
94
94
|
|
|
@@ -97,7 +97,7 @@ class AzureException(OSDException):
|
|
|
97
97
|
self,
|
|
98
98
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
99
99
|
error: str = None,
|
|
100
|
-
status_code: str =
|
|
100
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
101
101
|
):
|
|
102
102
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
103
103
|
|
|
@@ -106,7 +106,7 @@ class RedisException(OSDException):
|
|
|
106
106
|
self,
|
|
107
107
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
108
108
|
error: str = None,
|
|
109
|
-
status_code: str =
|
|
109
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
110
110
|
):
|
|
111
111
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
112
112
|
|
|
@@ -115,7 +115,7 @@ class ValidationDataException(OSDException):
|
|
|
115
115
|
self,
|
|
116
116
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
117
117
|
error: str = None,
|
|
118
|
-
status_code: str =
|
|
118
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
119
119
|
):
|
|
120
120
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
121
121
|
|
|
@@ -124,7 +124,7 @@ class UnexpectedException(OSDException):
|
|
|
124
124
|
self,
|
|
125
125
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
126
126
|
error: str = None,
|
|
127
|
-
status_code: str =
|
|
127
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
128
128
|
):
|
|
129
129
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
130
130
|
|
|
@@ -133,7 +133,7 @@ class MissingFieldException(OSDException):
|
|
|
133
133
|
self,
|
|
134
134
|
message: str = Message.MISSING_FIELD_ERROR_MSG,
|
|
135
135
|
error: str = None,
|
|
136
|
-
status_code: str =
|
|
136
|
+
status_code: str = StatusCode.VALIDATION_WARNING,
|
|
137
137
|
):
|
|
138
138
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
139
139
|
|
|
@@ -142,7 +142,7 @@ class ProfilePermissionDeniedException(OSDException):
|
|
|
142
142
|
self,
|
|
143
143
|
message: str = Message.PROFILE_PERMISSION_DENIED_MSG,
|
|
144
144
|
error: str = None,
|
|
145
|
-
status_code: str =
|
|
145
|
+
status_code: str = StatusCode.BUSINESS_RULE_WARNING,
|
|
146
146
|
):
|
|
147
147
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
148
148
|
|
|
@@ -151,7 +151,7 @@ class InvalidFormatException(OSDException):
|
|
|
151
151
|
self,
|
|
152
152
|
message: str = Message.INVALID_FORMAT_MSG,
|
|
153
153
|
error: str = None,
|
|
154
|
-
status_code: str =
|
|
154
|
+
status_code: str = StatusCode.VALIDATION_WARNING,
|
|
155
155
|
):
|
|
156
156
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
157
157
|
|
|
@@ -160,6 +160,6 @@ class HttpClientException(OSDException):
|
|
|
160
160
|
self,
|
|
161
161
|
message: str = Message.UNEXPECTED_ERROR_MSG,
|
|
162
162
|
error: str = None,
|
|
163
|
-
status_code: str =
|
|
163
|
+
status_code: str = StatusCode.INTERNAL_SERVER_ERROR,
|
|
164
164
|
):
|
|
165
165
|
super().__init__(message=message, error=error, status_code=status_code)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from azure.core.exceptions import (
|
|
2
3
|
HttpResponseError,
|
|
3
4
|
ResourceNotFoundError,
|
|
@@ -21,6 +22,9 @@ def classify(exc: Exception) -> Exception:
|
|
|
21
22
|
Convierte excepciones del SDK de Azure en AzureTransientError
|
|
22
23
|
o AzurePermanentError según si tiene sentido reintentar.
|
|
23
24
|
"""
|
|
25
|
+
if isinstance(exc, asyncio.TimeoutError):
|
|
26
|
+
return AzureTransientError(str(exc))
|
|
27
|
+
|
|
24
28
|
# Fallos de red/conexión → siempre transitorio
|
|
25
29
|
if isinstance(exc, (ServiceRequestError, ServiceResponseError)):
|
|
26
30
|
return AzureTransientError(str(exc))
|