Encryptors 2.42__tar.gz → 2.44__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.
Files changed (89) hide show
  1. {encryptors-2.42 → encryptors-2.44}/PKG-INFO +1 -1
  2. {encryptors-2.42 → encryptors-2.44}/setup.py +1 -1
  3. {encryptors-2.42 → encryptors-2.44}/src/Encryptors.egg-info/PKG-INFO +1 -1
  4. encryptors-2.44/src/Osdental/Decorators/SecureResolver.py +59 -0
  5. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/Extensions/AuditExtension.py +39 -49
  6. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/_Helpers/_AuditHelper.py +1 -1
  7. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Helpers/AuditDispatcher.py +6 -5
  8. encryptors-2.42/src/Osdental/Decorators/SecureResolver.py +0 -56
  9. {encryptors-2.42 → encryptors-2.44}/README.md +0 -0
  10. {encryptors-2.42 → encryptors-2.44}/setup.cfg +0 -0
  11. {encryptors-2.42 → encryptors-2.44}/src/Encryptors.egg-info/SOURCES.txt +0 -0
  12. {encryptors-2.42 → encryptors-2.44}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  13. {encryptors-2.42 → encryptors-2.44}/src/Encryptors.egg-info/entry_points.txt +0 -0
  14. {encryptors-2.42 → encryptors-2.44}/src/Encryptors.egg-info/requires.txt +0 -0
  15. {encryptors-2.42 → encryptors-2.44}/src/Encryptors.egg-info/top_level.txt +0 -0
  16. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Cli/__init__.py +0 -0
  17. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Database/BaseRepository.py +0 -0
  18. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Database/Connection.py +0 -0
  19. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Database/__init__.py +0 -0
  20. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Decorators/Grpc.py +0 -0
  21. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Decorators/PublicResolver.py +0 -0
  22. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Decorators/Retry.py +0 -0
  23. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Decorators/SqlDataNormalizer.py +0 -0
  24. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Decorators/__init__.py +0 -0
  25. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/Aes.py +0 -0
  26. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/Argon2.py +0 -0
  27. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/Bcrypt.py +0 -0
  28. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/Jwt.py +0 -0
  29. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/Rsa.py +0 -0
  30. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/Sha512.py +0 -0
  31. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Encryptor/__init__.py +0 -0
  32. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Exception/ControlledException.py +0 -0
  33. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Exception/__init__.py +0 -0
  34. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/Extensions/__init__.py +0 -0
  35. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/Models/__init__.py +0 -0
  36. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/_Exceptions/__init__.py +0 -0
  37. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +0 -0
  38. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +0 -0
  39. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/_Helpers/_TokenService.py +0 -0
  40. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/_Helpers/__init__.py +0 -0
  41. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Graphql/__init__.py +0 -0
  42. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Helpers/KeyVaultService.py +0 -0
  43. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Helpers/ResponseDecryptor.py +0 -0
  44. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Helpers/__init__.py +0 -0
  45. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Http/APIClient.py +0 -0
  46. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Http/_Exceptions.py +0 -0
  47. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Http/__init__.py +0 -0
  48. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
  49. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Messaging/Kafka.py +0 -0
  50. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Messaging/RabbitMQ.py +0 -0
  51. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Messaging/__init__.py +0 -0
  52. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Models/AuditConfig.py +0 -0
  53. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Models/Response.py +0 -0
  54. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Models/Token.py +0 -0
  55. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Models/_Audit.py +0 -0
  56. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Models/__init__.py +0 -0
  57. {encryptors-2.42 → encryptors-2.44}/src/Osdental/RedisCache/Redis.py +0 -0
  58. {encryptors-2.42 → encryptors-2.44}/src/Osdental/RedisCache/__init__.py +0 -0
  59. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Rest/Context/RequestContext.py +0 -0
  60. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Rest/Context/__init__.py +0 -0
  61. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +0 -0
  62. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Rest/Middlewares/__init__.py +0 -0
  63. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Rest/__init__.py +0 -0
  64. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/Code.py +0 -0
  65. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/Constant.py +0 -0
  66. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/FileType.py +0 -0
  67. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/GrahpqlOperation.py +0 -0
  68. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/Message.py +0 -0
  69. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/Profile.py +0 -0
  70. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Enums/__init__.py +0 -0
  71. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Logger.py +0 -0
  72. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/CaseConverter.py +0 -0
  73. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/CodeGenerator.py +0 -0
  74. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/DataNormalizer.py +0 -0
  75. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/DataUtils.py +0 -0
  76. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/DateUtils.py +0 -0
  77. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/FileMetaData.py +0 -0
  78. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/HashValidator.py +0 -0
  79. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/Mapper.py +0 -0
  80. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -0
  81. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/QueryGenerator.py +0 -0
  82. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/RsaUtils.py +0 -0
  83. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/TextProcessor.py +0 -0
  84. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/Utils/__init__.py +0 -0
  85. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Shared/__init__.py +0 -0
  86. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
  87. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Storage/S3Storage.py +0 -0
  88. {encryptors-2.42 → encryptors-2.44}/src/Osdental/Storage/__init__.py +0 -0
  89. {encryptors-2.42 → encryptors-2.44}/src/Osdental/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.42
3
+ Version: 2.44
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -2,7 +2,7 @@ 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.42",
5
+ version="2.44",
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.42
3
+ Version: 2.44
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -0,0 +1,59 @@
1
+ from functools import wraps
2
+ from typing import Callable
3
+ from Osdental.Models.Response import Response
4
+ from Osdental.Shared.Enums.Profile import Profile
5
+ from Osdental.Exception.ControlledException import OSDException, AccessDeniedException, UnauthorizedException
6
+ from Osdental.Shared.Logger import logger
7
+
8
+ def resolver(public: bool = False, action=None):
9
+
10
+ def decorator(func: Callable):
11
+
12
+ func._is_public = public
13
+
14
+ @wraps(func)
15
+ async def wrapper(obj, info, **kwargs):
16
+ try:
17
+ context = info.context
18
+ token = getattr(context, "token", None)
19
+
20
+ if not public:
21
+ if not token:
22
+ raise UnauthorizedException(
23
+ error="Authorization required"
24
+ )
25
+
26
+ if action:
27
+ if Profile(token.abbreviation) not in action.allowed_roles:
28
+ raise AccessDeniedException(
29
+ error="User not allowed to perform this action"
30
+ )
31
+
32
+ result = await func(obj, info, **kwargs)
33
+
34
+ if not isinstance(result, Response):
35
+ raise TypeError("Resolver must return a Response instance")
36
+
37
+ return result
38
+
39
+ except OSDException as e:
40
+ logger.warning(f"Business error: {str(e)}")
41
+
42
+ return Response(
43
+ status=e.status_code,
44
+ message=e.message,
45
+ error=e.error
46
+ )
47
+
48
+ except Exception as e:
49
+ logger.exception(f"Unexpected error: {str(e)}")
50
+
51
+ return Response(
52
+ status="DB_ERROR_UNEXPECTED",
53
+ message="Could not process request.",
54
+ error=str(e)
55
+ )
56
+
57
+ return wrapper
58
+
59
+ return decorator
@@ -1,5 +1,5 @@
1
+ import inspect
1
2
  import json
2
- import copy
3
3
  from graphql.pyutils import is_awaitable
4
4
  from ariadne.types import Extension
5
5
  from Osdental.Shared.Logger import logger
@@ -9,8 +9,8 @@ from Osdental.Rest.Context.RequestContext import (
9
9
  current_context,
10
10
  RequestContext
11
11
  )
12
+ from Osdental.Models.Response import Response
12
13
  from Osdental.Graphql.Models import BaseGraphQLContext
13
- from Osdental.Helpers.ResponseDecryptor import encryptor_data, VALID_TYPES
14
14
  from Osdental.Shared.Enums.Constant import Constant
15
15
 
16
16
  class AuditExtension(Extension):
@@ -39,9 +39,7 @@ class AuditExtension(Extension):
39
39
  request = context.request
40
40
 
41
41
  # cache body
42
- if not hasattr(context, "_cached_body"):
43
- context._cached_body = await request.json()
44
-
42
+ context._cached_body = getattr(context, "_cached_body", None) or await request.json()
45
43
  body = context._cached_body
46
44
 
47
45
  # skip introspection
@@ -57,7 +55,7 @@ class AuditExtension(Extension):
57
55
  if is_root:
58
56
 
59
57
  # identificar resolver público
60
- resolver_fn = getattr(next_, "__wrapped__", next_)
58
+ resolver_fn = inspect.unwrap(next_)
61
59
  is_public = getattr(resolver_fn, "_is_public", False)
62
60
 
63
61
  # inicializar auth UNA SOLA VEZ
@@ -68,27 +66,20 @@ class AuditExtension(Extension):
68
66
  container = context.container
69
67
  token_service = container.token_service
70
68
 
71
- aes_auth = request.app.state.aes_auth
72
- aes_user = request.app.state.aes_user
73
-
74
- if not aes_auth or not aes_user:
75
- aes_auth = None
76
- aes_user = None
77
-
78
69
  original_token = None
70
+ context.aes_auth = None
79
71
  if not is_public and headers.get("authorization"):
72
+ aes_auth = request.app.state.aes_auth
73
+ aes_user = request.app.state.aes_user
74
+
80
75
  try:
81
76
  original_token = await token_service.authenticate(headers, aes_user)
82
77
  except Exception:
83
78
  original_token = None
84
79
 
85
- context._original_token = original_token
86
- context.token = original_token
87
- context.aes_auth = aes_auth
80
+ context.aes_auth = aes_auth
88
81
 
89
82
  # VALIDACIÓN SOLO ROOT
90
- original_token = getattr(context, "_original_token", None)
91
-
92
83
  if not is_public and not original_token:
93
84
  raise ValueError("Authorization required")
94
85
 
@@ -102,30 +93,30 @@ class AuditExtension(Extension):
102
93
 
103
94
  decrypted_payload = None
104
95
 
105
- if encrypted_payload is not None:
106
- if isinstance(encrypted_payload, dict):
107
- decrypted_payload = encrypted_payload
96
+ if not is_public and encrypted_payload is not None:
108
97
 
109
- elif isinstance(encrypted_payload, str):
110
- try:
111
- decrypted_payload = json.loads(encrypted_payload)
112
- except Exception:
113
- if context.aes_auth:
114
- try:
115
- decrypted_payload = AES.decrypt(context.aes_auth, encrypted_payload)
116
- try:
117
- decrypted_payload = json.loads(decrypted_payload)
118
- except Exception:
119
- pass
120
- except Exception:
121
- decrypted_payload = None
98
+ if not isinstance(encrypted_payload, str):
99
+ raise ValueError("Encrypted payload must be a string")
122
100
 
101
+ if not context.aes_auth:
102
+ raise ValueError("Missing AES configuration")
103
+
104
+ try:
105
+ decrypted = AES.decrypt(context.aes_auth, encrypted_payload)
106
+ except Exception:
107
+ raise ValueError("Invalid encrypted payload")
108
+
109
+ try:
110
+ decrypted_payload = json.loads(decrypted)
111
+ except Exception:
112
+ raise ValueError("Decrypted payload is not valid JSON")
113
+
123
114
  if decrypted_payload is not None:
124
- context.decrypted_payload = decrypted_payload
115
+ kwargs["data"] = decrypted_payload
125
116
 
126
117
  if not is_public:
127
118
  token = TenantPolicy.resolve(
128
- token=context._original_token,
119
+ token=original_token,
129
120
  headers=request.headers,
130
121
  decrypted_payload=decrypted_payload,
131
122
  operation_type=info.operation.operation.value
@@ -155,13 +146,6 @@ class AuditExtension(Extension):
155
146
  if is_root and self.result is None:
156
147
  self.result = result
157
148
 
158
- if not getattr(context, "audit_plain_response", None):
159
- if result.encryption_type in VALID_TYPES and result.key:
160
- result_copy = copy.deepcopy(result)
161
- result = encryptor_data(result.encryption_type, result.key, result.data)
162
-
163
- context.audit_plain_response = result_copy
164
-
165
149
  return result
166
150
 
167
151
  def has_errors(self, errors, context):
@@ -174,13 +158,16 @@ class AuditExtension(Extension):
174
158
 
175
159
  query = self.request_payload.get("query", "")
176
160
 
177
- if "__schema" in query or "__type" in query:
161
+ if self._should_skip({"query": query}):
162
+ return
163
+
164
+ if self.result is None:
178
165
  return
179
166
 
180
- final_result = context.audit_plain_response or self.result
167
+ decrypted_key = context.aes_auth
181
168
 
182
- if final_result is None:
183
- return
169
+ if isinstance(self.result, Response):
170
+ decrypted_key = self.result.key or decrypted_key
184
171
 
185
172
  dispatcher = context.request.app.state.audit_dispatcher
186
173
 
@@ -188,8 +175,11 @@ class AuditExtension(Extension):
188
175
  dispatcher.dispatch(
189
176
  request=self.request,
190
177
  request_payload=self.request_payload,
191
- result=final_result,
178
+ result=self.result,
179
+ metadata={
180
+ "decrypted_key": decrypted_key
181
+ },
192
182
  audit_type=Constant.MESSAGE_LOG_INTERNAL
193
183
  )
194
184
  except Exception as e:
195
- logger(f"[AUDIT ERROR]: {str(e)}")
185
+ logger.warning(f"[AUDIT ERROR]: {str(e)}")
@@ -14,7 +14,7 @@ class AuditHelper:
14
14
 
15
15
  user_ip = request.headers.get("X-Forwarded-For")
16
16
  if user_ip:
17
- user_ip = user_ip.split(',')[0]
17
+ user_ip = user_ip.split(",")[0]
18
18
  else:
19
19
  user_ip = getattr(request.client, "host", "*")
20
20
 
@@ -40,6 +40,7 @@ class AuditDispatcher:
40
40
  request: Request,
41
41
  request_payload: Dict[str, Any],
42
42
  result: Response,
43
+ metadata: Dict[str, Any],
43
44
  audit_type: str = None
44
45
  ):
45
46
  try:
@@ -47,6 +48,7 @@ class AuditDispatcher:
47
48
  "request": request,
48
49
  "request_payload": request_payload,
49
50
  "result": result,
51
+ "metadata": metadata,
50
52
  "audit_type": audit_type
51
53
  }
52
54
 
@@ -77,6 +79,7 @@ class AuditDispatcher:
77
79
  request_payload = payload["request_payload"]
78
80
  result: Response = payload["result"]
79
81
  audit_type = payload["audit_type"]
82
+ metadata = payload["metadata"]
80
83
 
81
84
  operation_name = request_payload.get("operation_name")
82
85
  if operation_name == 'UnknownOperation':
@@ -100,9 +103,8 @@ class AuditDispatcher:
100
103
 
101
104
  # Obtencion de campos adicionales cuando es otro tipo de encriptacion o clave
102
105
  encryption_type = result.encryption_type
103
- key = result.key
106
+ decrypted_key = metadata.get("decrypted_key")
104
107
 
105
-
106
108
  if audit_type == Constant.MESSAGE_LOG_INTERNAL:
107
109
 
108
110
  ERROR_PREFIXES = ("DB_ERROR", "DB_WARNING")
@@ -116,7 +118,6 @@ class AuditDispatcher:
116
118
  except ValueError:
117
119
  is_error = True
118
120
 
119
-
120
121
  if is_error:
121
122
  payload = AuditHelper.build_final_payload(
122
123
  _type="ERROR",
@@ -127,8 +128,8 @@ class AuditDispatcher:
127
128
  audit_message = request_audit_payload | payload
128
129
 
129
130
  else:
130
- if encryption_type in VALID_TYPES and key:
131
- data = decryptor_data(encryption_type, key, data)
131
+ if encryption_type in VALID_TYPES and decrypted_key:
132
+ data = decryptor_data(encryption_type, decrypted_key, data)
132
133
 
133
134
  payload = AuditHelper.build_final_payload(
134
135
  _type="RESPONSE",
@@ -1,56 +0,0 @@
1
- import inspect
2
- import copy
3
- from functools import wraps
4
- from typing import Callable, Dict, Any
5
- from Osdental.Models.Token import AuthToken
6
- from Osdental.Models.Response import Response
7
- from Osdental.Shared.Enums.Profile import Profile
8
- from Osdental.Exception.ControlledException import OSDException, AccessDeniedException
9
- from Osdental.Encryptor.Aes import AES
10
- from Osdental.Graphql.Models import BaseGraphQLContext
11
- from Osdental.Shared.Logger import logger
12
-
13
- def secure_resolver(action = None):
14
- def decorator(func: Callable):
15
-
16
- signature = inspect.signature(func)
17
- params = signature.parameters
18
-
19
- @wraps(func)
20
- async def wrapper(obj: Any, context: BaseGraphQLContext, **kwargs: Dict[str, Any]):
21
- try:
22
- token: AuthToken = context.token
23
- payload = getattr(context, "decrypted_payload", None)
24
- aes_auth = getattr(context, "aes_auth", None)
25
-
26
- if action:
27
- if Profile(token.abbreviation) not in action.allowed_roles:
28
- raise AccessDeniedException(error="The user's profile is not allowed to perform this action.")
29
-
30
- if "token" in params:
31
- kwargs["token"] = token
32
-
33
- if "data" in params:
34
- kwargs["data"] = payload
35
-
36
- result: Response = await func(obj, **kwargs)
37
-
38
- context.audit_plain_response = copy.deepcopy(result)
39
-
40
- if result.data is not None and result.encryption_type == "AES" and result.key is None:
41
- result.data = AES.encrypt(aes_auth, result.data)
42
-
43
- return result.send()
44
-
45
- except (Exception, OSDException) as e:
46
- logger.exception(f"An error has occurred: {str(e)}")
47
- result = Response(
48
- status=getattr(e, "status_code", "DB_ERROR_UNEXPECTED"),
49
- message=getattr(e, "message", "Could not process request."),
50
- error=f"{type(e).__name__}: {str(e)}"
51
- )
52
- context.audit_plain_response = result
53
- return result.send()
54
-
55
- return wrapper
56
- return decorator
File without changes
File without changes