Encryptors 2.77__tar.gz → 2.79__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 (90) hide show
  1. {encryptors-2.77 → encryptors-2.79}/PKG-INFO +1 -1
  2. {encryptors-2.77 → encryptors-2.79}/setup.py +1 -1
  3. {encryptors-2.77 → encryptors-2.79}/src/Encryptors.egg-info/PKG-INFO +1 -1
  4. {encryptors-2.77 → encryptors-2.79}/src/Encryptors.egg-info/SOURCES.txt +1 -0
  5. encryptors-2.79/src/Osdental/Decorators/PublicResolver.py +139 -0
  6. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Decorators/SecureResolver.py +31 -38
  7. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/AuditQueue.py +5 -2
  8. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/AuthTokenProcessor.py +2 -2
  9. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/Graphql.py +1 -0
  10. {encryptors-2.77 → encryptors-2.79}/README.md +0 -0
  11. {encryptors-2.77 → encryptors-2.79}/setup.cfg +0 -0
  12. {encryptors-2.77 → encryptors-2.79}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  13. {encryptors-2.77 → encryptors-2.79}/src/Encryptors.egg-info/entry_points.txt +0 -0
  14. {encryptors-2.77 → encryptors-2.79}/src/Encryptors.egg-info/requires.txt +0 -0
  15. {encryptors-2.77 → encryptors-2.79}/src/Encryptors.egg-info/top_level.txt +0 -0
  16. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Cache/Redis.py +0 -0
  17. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Cache/__init__.py +0 -0
  18. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Cli/__init__.py +0 -0
  19. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Constants/Constant.py +0 -0
  20. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Constants/Message.py +0 -0
  21. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Constants/__init__.py +0 -0
  22. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Context/__init__.py +0 -0
  23. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Database/BaseRepository.py +0 -0
  24. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Database/Connection.py +0 -0
  25. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Database/__init__.py +0 -0
  26. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Decorators/Grpc.py +0 -0
  27. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Decorators/Retry.py +0 -0
  28. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Decorators/__init__.py +0 -0
  29. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/Aes.py +0 -0
  30. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/Argon2.py +0 -0
  31. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/Bcrypt.py +0 -0
  32. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/Jwt.py +0 -0
  33. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/Rsa.py +0 -0
  34. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/Sha512.py +0 -0
  35. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Encryptor/__init__.py +0 -0
  36. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/AuditType.py +0 -0
  37. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/CipherType.py +0 -0
  38. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/FileType.py +0 -0
  39. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/GrahpqlOperation.py +0 -0
  40. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/GrpcStatusCode.py +0 -0
  41. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/Profile.py +0 -0
  42. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/ResultType.py +0 -0
  43. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/Secret.py +0 -0
  44. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/StatusCode.py +0 -0
  45. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Enums/__init__.py +0 -0
  46. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Exception/ControlledException.py +0 -0
  47. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Exception/__init__.py +0 -0
  48. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/AuditHelper.py +0 -0
  49. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/AzureClassifier.py +0 -0
  50. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/CipherHelper.py +0 -0
  51. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/JwtTokenHelper.py +0 -0
  52. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/Resilience.py +0 -0
  53. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Helpers/__init__.py +0 -0
  54. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Http/APIClient.py +0 -0
  55. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Http/__init__.py +0 -0
  56. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
  57. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Messaging/Kafka.py +0 -0
  58. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Messaging/RabbitMQ.py +0 -0
  59. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Messaging/__init__.py +0 -0
  60. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/ApiResponse.py +0 -0
  61. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/AuditContext.py +0 -0
  62. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/Notification.py +0 -0
  63. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/Response.py +0 -0
  64. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/Token.py +0 -0
  65. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/TokenClaims.py +0 -0
  66. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Models/__init__.py +0 -0
  67. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Secrets/AzureKeyVaultProvider.py +0 -0
  68. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Secrets/__init__.py +0 -0
  69. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Services/GrpcConnection.py +0 -0
  70. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Services/JwtAuthTokenService.py +0 -0
  71. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Services/ServiceBusAuditEmitter.py +0 -0
  72. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Services/WebsocketClient.py +0 -0
  73. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Services/__init__.py +0 -0
  74. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
  75. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Storage/S3Storage.py +0 -0
  76. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Storage/__init__.py +0 -0
  77. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/CaseConverter.py +0 -0
  78. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/CodeGenerator.py +0 -0
  79. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/DataNormalizer.py +0 -0
  80. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/DataUtils.py +0 -0
  81. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/DateUtils.py +0 -0
  82. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/FileMetaData.py +0 -0
  83. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/HashValidator.py +0 -0
  84. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/Mapper.py +0 -0
  85. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/PasswordGenerator.py +0 -0
  86. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/QueryGenerator.py +0 -0
  87. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/RsaUtils.py +0 -0
  88. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/TextProcessor.py +0 -0
  89. {encryptors-2.77 → encryptors-2.79}/src/Osdental/Utils/__init__.py +0 -0
  90. {encryptors-2.77 → encryptors-2.79}/src/Osdental/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.77
3
+ Version: 2.79
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
 
3
3
  setup(
4
4
  name="Encryptors",
5
- version="2.77",
5
+ version="2.79",
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.77
3
+ Version: 2.79
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -18,6 +18,7 @@ src/Osdental/Database/BaseRepository.py
18
18
  src/Osdental/Database/Connection.py
19
19
  src/Osdental/Database/__init__.py
20
20
  src/Osdental/Decorators/Grpc.py
21
+ src/Osdental/Decorators/PublicResolver.py
21
22
  src/Osdental/Decorators/Retry.py
22
23
  src/Osdental/Decorators/SecureResolver.py
23
24
  src/Osdental/Decorators/__init__.py
@@ -0,0 +1,139 @@
1
+ import logging
2
+ import asyncio
3
+ from functools import wraps
4
+ from typing import Callable
5
+ from graphql import GraphQLResolveInfo
6
+ from Osdental.Models.Response import Response
7
+ from Osdental.Models.Graphql import BaseGraphQLContext
8
+ from Osdental.Models.TokenClaims import AccessTokenClaims
9
+ from Osdental.Exception.ControlledException import (
10
+ OSDException
11
+ )
12
+ from Osdental.Services import IAuthTokenService
13
+ from Osdental.Helpers.AuthTokenProcessor import (
14
+ decrypt_and_parse_payload, get_current_user,
15
+ get_current_aes_key
16
+ )
17
+ from Osdental.Enums.StatusCode import StatusCode
18
+ from Osdental.Models.AuditContext import AuditContext
19
+ from Osdental.Helpers.AuditHelper import (
20
+ _build_success_payload, _build_error_payload, _build_unexpected_payload
21
+ )
22
+ from Osdental.Context import _ctx
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def public_resolver():
28
+
29
+ def decorator(func: Callable):
30
+
31
+ @wraps(func)
32
+ async def wrapper(obj, info: GraphQLResolveInfo, **kwargs):
33
+ try:
34
+ context: BaseGraphQLContext = info.context
35
+ container = context.container
36
+ request = context.request
37
+ headers = request.headers
38
+ settings = container.settings
39
+
40
+ access_token = headers.get("Accesstoken")
41
+ claims: AccessTokenClaims | None = None
42
+ audit_ctx: AuditContext | None = None
43
+ operation_type = info.operation.operation.value
44
+
45
+ # ── 1. AUTENTICACIÓN ──────────────────────────────
46
+ token_service: IAuthTokenService = container.auth_token_service
47
+
48
+ claims = await token_service.validate_external_access_token(
49
+ access_token
50
+ )
51
+ context.access_token = access_token
52
+ context.access_token_claims = claims
53
+
54
+ # ── 2. DECRYPT DEL PAYLOAD ────────────────────────
55
+ body = await request.json()
56
+ encrypted_payload = (
57
+ kwargs.get("data")
58
+ or body.get("variables", {}).get("data")
59
+ )
60
+ decrypted_payload = None
61
+
62
+ if encrypted_payload is not None:
63
+ decrypted_payload = decrypt_and_parse_payload(
64
+ aes_key_auth=claims.aes_key,
65
+ encrypted_payload=encrypted_payload
66
+ )
67
+
68
+ kwargs["data"] = decrypted_payload
69
+
70
+
71
+ # ── 5. CONTEXTO DE AUDITORÍA ──────────────
72
+ # Lo construimos antes del resolver para que siempre exista
73
+ user = get_current_user(context)
74
+ key = get_current_aes_key(context)
75
+ audit_ctx = AuditContext.from_request(
76
+ request=request,
77
+ settings=settings,
78
+ body=body,
79
+ operation_type=operation_type,
80
+ id_user=user,
81
+ decrypted_key=key,
82
+ decrypted_payload=decrypted_payload,
83
+ )
84
+
85
+ _ctx.set(audit_ctx)
86
+
87
+ # ── 6. EJECUTAR RESOLVER ──────────────────
88
+ result = await func(obj, info, **kwargs)
89
+
90
+ if not isinstance(result, Response):
91
+ raise TypeError("Resolver must return a Response instance")
92
+
93
+ # ── 7. AUDITAR ÉXITO ──────────────────────
94
+ _ = asyncio.create_task(
95
+ container.audit_emitter.emit(
96
+ {
97
+ "payload": _build_success_payload(audit_ctx, result),
98
+ "is_auditable": settings.is_auditable,
99
+ }
100
+ )
101
+ )
102
+
103
+ return result
104
+
105
+ except OSDException as e:
106
+ logger.warning(f"Business error: {str(e)}")
107
+ if audit_ctx:
108
+ _ = asyncio.create_task(
109
+ container.audit_emitter.emit(
110
+ {
111
+ "payload": _build_error_payload(audit_ctx, e),
112
+ "is_auditable": settings.is_auditable,
113
+ }
114
+ )
115
+ )
116
+ return Response(
117
+ status=e.status_code,
118
+ message=e.message,
119
+ error=e.error
120
+ )
121
+ except Exception as e:
122
+ logger.exception(f"Unexpected error: {str(e)}")
123
+ if audit_ctx:
124
+ _ = asyncio.create_task(
125
+ container.audit_emitter.emit(
126
+ {
127
+ "payload": _build_unexpected_payload(audit_ctx, e),
128
+ "is_auditable": settings.is_auditable,
129
+ }
130
+ )
131
+ )
132
+ return Response(
133
+ status=StatusCode.INTERNAL_SERVER_ERROR,
134
+ message="Could not process request.",
135
+ error=str(e)
136
+ )
137
+
138
+ return wrapper
139
+ return decorator
@@ -6,7 +6,7 @@ from graphql import GraphQLResolveInfo
6
6
  from Osdental.Models.Response import Response
7
7
  from Osdental.Models.Graphql import BaseGraphQLContext
8
8
  from Osdental.Models.TokenClaims import (
9
- UserTokenClaims, AccessTokenClaims
9
+ UserTokenClaims
10
10
  )
11
11
  from Osdental.Models.Token import AuthToken
12
12
  from Osdental.Enums.Profile import Profile
@@ -28,10 +28,9 @@ from Osdental.Context import _ctx
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
30
 
31
- def resolver(public: bool = False, action=None):
31
+ def resolver(action=None):
32
32
 
33
33
  def decorator(func: Callable):
34
- func._is_public = public
35
34
 
36
35
  @wraps(func)
37
36
  async def wrapper(obj, info: GraphQLResolveInfo, **kwargs):
@@ -42,29 +41,19 @@ def resolver(public: bool = False, action=None):
42
41
  headers = request.headers
43
42
  settings = container.settings
44
43
 
45
- access_token = headers.get("Accesstoken")
46
44
  token: type[AuthToken] | None = None
47
- aes_key = None
48
- claims: UserTokenClaims | AccessTokenClaims | None = None
45
+ claims: UserTokenClaims | None = None
49
46
  audit_ctx: AuditContext | None = None
50
47
  operation_type = info.operation.operation.value
51
48
 
52
49
  # ── 1. AUTENTICACIÓN ──────────────────────────────
53
50
  token_service: IAuthTokenService = container.auth_token_service
54
- if not public:
55
- user_token = extract_bearer_token(headers)
56
- claims = await token_service.validate_internal_access_token(
57
- user_token
58
- )
59
- aes_key = claims.aes_key_auth
60
-
61
- elif public and access_token:
62
- claims = await token_service.validate_external_access_token(
63
- access_token
64
- )
65
- aes_key = claims.aes_key
66
- context.access_token = access_token
67
- context.access_token_claims = claims
51
+ user_token = extract_bearer_token(headers)
52
+ context.user_token = user_token
53
+
54
+ claims = await token_service.validate_internal_access_token(
55
+ user_token
56
+ )
68
57
 
69
58
  # ── 2. DECRYPT DEL PAYLOAD ────────────────────────
70
59
  body = await request.json()
@@ -76,26 +65,24 @@ def resolver(public: bool = False, action=None):
76
65
 
77
66
  if encrypted_payload is not None:
78
67
  decrypted_payload = decrypt_and_parse_payload(
79
- aes_key_auth=aes_key,
68
+ aes_key_auth=claims.aes_key_auth,
80
69
  encrypted_payload=encrypted_payload
81
70
  )
82
71
 
83
72
  kwargs["data"] = decrypted_payload
84
73
 
85
74
  # ── 3. TENANT POLICY ──────────────────────────────
86
- if not public:
87
-
88
- token = build_auth_token(
89
- claims=claims,
90
- headers=request.headers,
91
- decrypted_payload=decrypted_payload,
92
- operation_type=operation_type,
93
- )
94
-
95
- context.token = token
75
+ token = build_auth_token(
76
+ claims=claims,
77
+ headers=request.headers,
78
+ decrypted_payload=decrypted_payload,
79
+ operation_type=operation_type,
80
+ )
81
+
82
+ context.token = token
96
83
 
97
84
  # ── 4. CONTROL DE ACCESO POR ROL ─────────────────
98
- if not public and action:
85
+ if action:
99
86
  if Profile(token.abbreviation) not in action.allowed_roles:
100
87
  raise AccessDeniedException(
101
88
  status_code=StatusCode.BUSINESS_RULE_WARNING,
@@ -127,8 +114,10 @@ def resolver(public: bool = False, action=None):
127
114
  # ── 7. AUDITAR ÉXITO ──────────────────────
128
115
  _ = asyncio.create_task(
129
116
  container.audit_emitter.emit(
130
- _build_success_payload(audit_ctx, result),
131
- settings.is_auditable,
117
+ {
118
+ "payload": _build_success_payload(audit_ctx, result),
119
+ "is_auditable": settings.is_auditable,
120
+ }
132
121
  )
133
122
  )
134
123
 
@@ -139,8 +128,10 @@ def resolver(public: bool = False, action=None):
139
128
  if audit_ctx:
140
129
  _ = asyncio.create_task(
141
130
  container.audit_emitter.emit(
142
- _build_error_payload(audit_ctx, e),
143
- settings.is_auditable,
131
+ {
132
+ "payload": _build_error_payload(audit_ctx, e),
133
+ "is_auditable": settings.is_auditable,
134
+ }
144
135
  )
145
136
  )
146
137
  return Response(
@@ -153,8 +144,10 @@ def resolver(public: bool = False, action=None):
153
144
  if audit_ctx:
154
145
  _ = asyncio.create_task(
155
146
  container.audit_emitter.emit(
156
- _build_unexpected_payload(audit_ctx, e),
157
- settings.is_auditable,
147
+ {
148
+ "payload": _build_unexpected_payload(audit_ctx, e),
149
+ "is_auditable": settings.is_auditable,
150
+ }
158
151
  )
159
152
  )
160
153
  return Response(
@@ -27,10 +27,13 @@ class AuditQueue:
27
27
 
28
28
  async def _consume(self):
29
29
  while True:
30
- payload = await self._queue.get()
30
+ item = await self._queue.get()
31
31
 
32
32
  try:
33
- await self._worker(payload)
33
+ await self._worker(
34
+ item["payload"],
35
+ item["is_auditable"],
36
+ )
34
37
 
35
38
  except Exception:
36
39
  logger.exception("Error processing audit")
@@ -10,11 +10,11 @@ from Osdental.Enums.Profile import Profile
10
10
  from Osdental.Models.TokenClaims import UserTokenClaims
11
11
  from Osdental.Models.Graphql import BaseGraphQLContext
12
12
 
13
- def extract_bearer_token(headers: Headers) -> str:
13
+ def extract_bearer_token(headers: Headers) -> str | None:
14
14
 
15
15
  authorization = headers.get("authorization")
16
16
  if not authorization or not authorization.startswith("Bearer "):
17
- raise ValueError("Missing Bearer token")
17
+ return None
18
18
 
19
19
  return authorization.removeprefix("Bearer ").strip()
20
20
 
@@ -10,4 +10,5 @@ class BaseGraphQLContext:
10
10
  container: Any
11
11
  access_token: str | None = None
12
12
  access_token_claims: AccessTokenClaims | None = None
13
+ user_token: str | None = None
13
14
  token: AuthToken | None = None
File without changes
File without changes