Encryptors 2.78__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.78 → encryptors-2.79}/PKG-INFO +1 -1
  2. {encryptors-2.78 → encryptors-2.79}/setup.py +1 -1
  3. {encryptors-2.78 → encryptors-2.79}/src/Encryptors.egg-info/PKG-INFO +1 -1
  4. {encryptors-2.78 → 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.78 → encryptors-2.79}/src/Osdental/Decorators/SecureResolver.py +19 -36
  7. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/AuthTokenProcessor.py +2 -2
  8. {encryptors-2.78 → encryptors-2.79}/README.md +0 -0
  9. {encryptors-2.78 → encryptors-2.79}/setup.cfg +0 -0
  10. {encryptors-2.78 → encryptors-2.79}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  11. {encryptors-2.78 → encryptors-2.79}/src/Encryptors.egg-info/entry_points.txt +0 -0
  12. {encryptors-2.78 → encryptors-2.79}/src/Encryptors.egg-info/requires.txt +0 -0
  13. {encryptors-2.78 → encryptors-2.79}/src/Encryptors.egg-info/top_level.txt +0 -0
  14. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Cache/Redis.py +0 -0
  15. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Cache/__init__.py +0 -0
  16. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Cli/__init__.py +0 -0
  17. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Constants/Constant.py +0 -0
  18. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Constants/Message.py +0 -0
  19. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Constants/__init__.py +0 -0
  20. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Context/__init__.py +0 -0
  21. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Database/BaseRepository.py +0 -0
  22. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Database/Connection.py +0 -0
  23. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Database/__init__.py +0 -0
  24. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Decorators/Grpc.py +0 -0
  25. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Decorators/Retry.py +0 -0
  26. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Decorators/__init__.py +0 -0
  27. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/Aes.py +0 -0
  28. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/Argon2.py +0 -0
  29. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/Bcrypt.py +0 -0
  30. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/Jwt.py +0 -0
  31. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/Rsa.py +0 -0
  32. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/Sha512.py +0 -0
  33. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Encryptor/__init__.py +0 -0
  34. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/AuditType.py +0 -0
  35. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/CipherType.py +0 -0
  36. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/FileType.py +0 -0
  37. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/GrahpqlOperation.py +0 -0
  38. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/GrpcStatusCode.py +0 -0
  39. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/Profile.py +0 -0
  40. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/ResultType.py +0 -0
  41. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/Secret.py +0 -0
  42. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/StatusCode.py +0 -0
  43. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Enums/__init__.py +0 -0
  44. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Exception/ControlledException.py +0 -0
  45. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Exception/__init__.py +0 -0
  46. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/AuditHelper.py +0 -0
  47. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/AuditQueue.py +0 -0
  48. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/AzureClassifier.py +0 -0
  49. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/CipherHelper.py +0 -0
  50. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/JwtTokenHelper.py +0 -0
  51. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/Resilience.py +0 -0
  52. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Helpers/__init__.py +0 -0
  53. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Http/APIClient.py +0 -0
  54. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Http/__init__.py +0 -0
  55. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
  56. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Messaging/Kafka.py +0 -0
  57. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Messaging/RabbitMQ.py +0 -0
  58. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Messaging/__init__.py +0 -0
  59. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/ApiResponse.py +0 -0
  60. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/AuditContext.py +0 -0
  61. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/Graphql.py +0 -0
  62. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/Notification.py +0 -0
  63. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/Response.py +0 -0
  64. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/Token.py +0 -0
  65. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/TokenClaims.py +0 -0
  66. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Models/__init__.py +0 -0
  67. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Secrets/AzureKeyVaultProvider.py +0 -0
  68. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Secrets/__init__.py +0 -0
  69. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Services/GrpcConnection.py +0 -0
  70. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Services/JwtAuthTokenService.py +0 -0
  71. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Services/ServiceBusAuditEmitter.py +0 -0
  72. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Services/WebsocketClient.py +0 -0
  73. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Services/__init__.py +0 -0
  74. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
  75. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Storage/S3Storage.py +0 -0
  76. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Storage/__init__.py +0 -0
  77. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/CaseConverter.py +0 -0
  78. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/CodeGenerator.py +0 -0
  79. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/DataNormalizer.py +0 -0
  80. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/DataUtils.py +0 -0
  81. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/DateUtils.py +0 -0
  82. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/FileMetaData.py +0 -0
  83. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/HashValidator.py +0 -0
  84. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/Mapper.py +0 -0
  85. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/PasswordGenerator.py +0 -0
  86. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/QueryGenerator.py +0 -0
  87. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/RsaUtils.py +0 -0
  88. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/TextProcessor.py +0 -0
  89. {encryptors-2.78 → encryptors-2.79}/src/Osdental/Utils/__init__.py +0 -0
  90. {encryptors-2.78 → 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.78
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.78",
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.78
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,14 +28,9 @@ from Osdental.Context import _ctx
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
30
 
31
- def resolver(
32
- public: bool = False,
33
- skip_token_validation: bool = False,
34
- action=None
35
- ):
31
+ def resolver(action=None):
36
32
 
37
33
  def decorator(func: Callable):
38
- func._is_public = public
39
34
 
40
35
  @wraps(func)
41
36
  async def wrapper(obj, info: GraphQLResolveInfo, **kwargs):
@@ -46,29 +41,19 @@ def resolver(
46
41
  headers = request.headers
47
42
  settings = container.settings
48
43
 
49
- access_token = headers.get("Accesstoken")
50
44
  token: type[AuthToken] | None = None
51
- aes_key = None
52
- claims: UserTokenClaims | AccessTokenClaims | None = None
45
+ claims: UserTokenClaims | None = None
53
46
  audit_ctx: AuditContext | None = None
54
47
  operation_type = info.operation.operation.value
55
48
 
56
49
  # ── 1. AUTENTICACIÓN ──────────────────────────────
57
50
  token_service: IAuthTokenService = container.auth_token_service
58
- if not public and not skip_token_validation:
59
- user_token = extract_bearer_token(headers)
60
- claims = await token_service.validate_internal_access_token(
61
- user_token
62
- )
63
- aes_key = claims.aes_key_auth
64
-
65
- elif public and access_token:
66
- claims = await token_service.validate_external_access_token(
67
- access_token
68
- )
69
- aes_key = claims.aes_key
70
- context.access_token = access_token
71
- 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
+ )
72
57
 
73
58
  # ── 2. DECRYPT DEL PAYLOAD ────────────────────────
74
59
  body = await request.json()
@@ -80,26 +65,24 @@ def resolver(
80
65
 
81
66
  if encrypted_payload is not None:
82
67
  decrypted_payload = decrypt_and_parse_payload(
83
- aes_key_auth=aes_key,
68
+ aes_key_auth=claims.aes_key_auth,
84
69
  encrypted_payload=encrypted_payload
85
70
  )
86
71
 
87
72
  kwargs["data"] = decrypted_payload
88
73
 
89
74
  # ── 3. TENANT POLICY ──────────────────────────────
90
- if not public:
91
-
92
- token = build_auth_token(
93
- claims=claims,
94
- headers=request.headers,
95
- decrypted_payload=decrypted_payload,
96
- operation_type=operation_type,
97
- )
98
-
99
- 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
100
83
 
101
84
  # ── 4. CONTROL DE ACCESO POR ROL ─────────────────
102
- if not public and action:
85
+ if action:
103
86
  if Profile(token.abbreviation) not in action.allowed_roles:
104
87
  raise AccessDeniedException(
105
88
  status_code=StatusCode.BUSINESS_RULE_WARNING,
@@ -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
 
File without changes
File without changes