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.
Files changed (100) hide show
  1. {encryptors-2.81.2 → encryptors-2.81.4}/PKG-INFO +1 -1
  2. {encryptors-2.81.2 → encryptors-2.81.4}/setup.py +2 -2
  3. {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/PKG-INFO +1 -1
  4. {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/SOURCES.txt +24 -26
  5. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Cli/__init__.py +7 -8
  6. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/BaseRepository.py +1 -1
  7. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/SqlExceptionTranslator.py +2 -4
  8. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/StoredProcedureResultValidator.py +1 -4
  9. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/Grpc.py +1 -1
  10. encryptors-2.81.4/src/Osdental/Decorators/SecureResolver.py +88 -0
  11. encryptors-2.81.4/src/Osdental/Encryptor/Aes.py +61 -0
  12. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/Bcrypt.py +4 -4
  13. encryptors-2.81.4/src/Osdental/Encryptor/Jwt.py +34 -0
  14. encryptors-2.81.4/src/Osdental/Encryptor/Rsa.py +47 -0
  15. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/Sha512.py +1 -2
  16. encryptors-2.81.4/src/Osdental/Enums/FileType.py +9 -0
  17. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Enums/GrahpqlOperation.py +2 -2
  18. encryptors-2.81.4/src/Osdental/Enums/GrpcStatusCode.py +25 -0
  19. encryptors-2.81.4/src/Osdental/Enums/StatusCode.py +40 -0
  20. encryptors-2.81.4/src/Osdental/Exception/ControlledException.py +49 -0
  21. encryptors-2.81.4/src/Osdental/Helpers/AuthorizationHelpers.py +53 -0
  22. encryptors-2.81.4/src/Osdental/Helpers/Logger.py +17 -0
  23. encryptors-2.81.4/src/Osdental/Helpers/RequestDecryption.py +33 -0
  24. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/ResponseDecryptor.py +1 -1
  25. encryptors-2.81.4/src/Osdental/Helpers/TokenValidationService.py +51 -0
  26. encryptors-2.81.4/src/Osdental/Http/HttpxClient.py +145 -0
  27. encryptors-2.81.4/src/Osdental/Http/__init__.py +69 -0
  28. encryptors-2.81.4/src/Osdental/Models/Response.py +11 -0
  29. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Models/Token.py +4 -5
  30. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Models/base_graphql_context.py +3 -3
  31. encryptors-2.81.4/src/Osdental/Utils/FileMetaData.py +50 -0
  32. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/Mapper.py +13 -4
  33. encryptors-2.81.4/src/Osdental/Utils/PasswordGenerator.py +58 -0
  34. encryptors-2.81.2/src/Osdental/Decorators/SecureResolver.py +0 -119
  35. encryptors-2.81.2/src/Osdental/Encryptor/Aes.py +0 -85
  36. encryptors-2.81.2/src/Osdental/Encryptor/Jwt.py +0 -43
  37. encryptors-2.81.2/src/Osdental/Encryptor/Rsa.py +0 -58
  38. encryptors-2.81.2/src/Osdental/Exception/ControlledException.py +0 -159
  39. encryptors-2.81.2/src/Osdental/Helpers/extract_auth_token.py +0 -11
  40. encryptors-2.81.2/src/Osdental/Helpers/tenant_policy.py +0 -46
  41. encryptors-2.81.2/src/Osdental/Helpers/token_validation_service.py +0 -49
  42. encryptors-2.81.2/src/Osdental/Http/HttpxClient.py +0 -128
  43. encryptors-2.81.2/src/Osdental/Http/__init__.py +0 -30
  44. encryptors-2.81.2/src/Osdental/Models/Response.py +0 -13
  45. encryptors-2.81.2/src/Osdental/Shared/Enums/Code.py +0 -26
  46. encryptors-2.81.2/src/Osdental/Shared/Enums/Constant.py +0 -10
  47. encryptors-2.81.2/src/Osdental/Shared/Enums/FileType.py +0 -9
  48. encryptors-2.81.2/src/Osdental/Shared/Enums/Message.py +0 -40
  49. encryptors-2.81.2/src/Osdental/Shared/Logger.py +0 -17
  50. encryptors-2.81.2/src/Osdental/Shared/Utils/FileMetaData.py +0 -50
  51. encryptors-2.81.2/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -45
  52. encryptors-2.81.2/src/Osdental/__init__.py +0 -0
  53. {encryptors-2.81.2 → encryptors-2.81.4}/README.md +0 -0
  54. {encryptors-2.81.2 → encryptors-2.81.4}/setup.cfg +0 -0
  55. {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  56. {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/entry_points.txt +0 -0
  57. {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/requires.txt +0 -0
  58. {encryptors-2.81.2 → encryptors-2.81.4}/src/Encryptors.egg-info/top_level.txt +0 -0
  59. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Cache/Redis.py +0 -0
  60. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Cache/__init__.py +0 -0
  61. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/Connection.py +0 -0
  62. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/SqlTransientDetector.py +0 -0
  63. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Database/__init__.py +0 -0
  64. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/PublicResolver.py +0 -0
  65. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/Retry.py +0 -0
  66. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Decorators/__init__.py +0 -0
  67. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/Argon2.py +0 -0
  68. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Encryptor/__init__.py +0 -0
  69. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Enums/Profile.py +0 -0
  70. {encryptors-2.81.2/src/Osdental/Exception → encryptors-2.81.4/src/Osdental/Enums}/__init__.py +0 -0
  71. {encryptors-2.81.2/src/Osdental/Helpers → encryptors-2.81.4/src/Osdental/Exception}/__init__.py +0 -0
  72. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/AzureClassifier.py +0 -0
  73. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/GrpcConnection.py +0 -0
  74. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Helpers/Resilience.py +0 -0
  75. {encryptors-2.81.2/src/Osdental/Models → encryptors-2.81.4/src/Osdental/Helpers}/__init__.py +0 -0
  76. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Constants.py +0 -0
  77. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Exceptions.py +0 -0
  78. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Messages.py +0 -0
  79. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Http/Types.py +0 -0
  80. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
  81. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/Kafka.py +0 -0
  82. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/RabbitMQ.py +0 -0
  83. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Messaging/__init__.py +0 -0
  84. {encryptors-2.81.2/src/Osdental/Shared/Enums → encryptors-2.81.4/src/Osdental/Models}/__init__.py +0 -0
  85. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Secrets/KeyVaultService.py +0 -0
  86. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Secrets/__init__.py +0 -0
  87. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
  88. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Storage/S3Storage.py +0 -0
  89. {encryptors-2.81.2 → encryptors-2.81.4}/src/Osdental/Storage/__init__.py +0 -0
  90. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/CaseConverter.py +0 -0
  91. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/CodeGenerator.py +0 -0
  92. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/DataNormalizer.py +0 -0
  93. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/DataUtils.py +0 -0
  94. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/DateUtils.py +0 -0
  95. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/HashValidator.py +0 -0
  96. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/QueryGenerator.py +0 -0
  97. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/RsaUtils.py +0 -0
  98. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/TextProcessor.py +0 -0
  99. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/Utils/__init__.py +0 -0
  100. {encryptors-2.81.2/src/Osdental/Shared → encryptors-2.81.4/src/Osdental}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.81.2
3
+ Version: 2.81.4
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -1,8 +1,8 @@
1
1
  from setuptools import setup, find_packages
2
- # ANDERSON REVISAR EL CACHE LOCAL DEL KEYVAULT PARA VALIDAR SI FUNCIONA
2
+
3
3
  setup(
4
4
  name="Encryptors",
5
- version="2.81.2",
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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.81.2
3
+ Version: 2.81.4
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -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.Shared.Logger import logger
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(Message.PYCACHE_CLEANUP_SUCCESS_MSG)
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'{Message.SERVER_NETWORK_ACCESS_ERROR_MSG}: {e}')
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'{Message.SERVER_NETWORK_ACCESS_ERROR_MSG}: {e}')
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(Message.REDIS_CLEANUP_SUCCESS_MSG)
54
+ logger.info("Redis cleanup completed successfully.")
56
55
  except Exception as e:
57
- logger.error(f'{Message.REDIS_CLEANUP_ERROR_MSG}: {e}')
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(Message.PROTO_FILES_GENERATED_MSG)
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.Shared.Utils.DataNormalizer import normalize
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
- Message.UNEXPECTED_ERROR_MSG,
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=Message.UNEXPECTED_ERROR_MSG,
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
  )
@@ -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
- message=status_message or Message.UNEXPECTED_ERROR_MSG,
60
- error=status_message or Message.UNEXPECTED_ERROR_MSG,
57
+ error=status_message,
61
58
  status_code=status_code,
62
59
  )
63
60
 
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from functools import wraps
3
- from Osdental.Shared.Logger import logger
3
+ from Osdental.Helpers.Logger import logger
4
4
 
5
5
  def parse_rpc_request_payload(func):
6
6
 
@@ -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
- from Osdental.Shared.Enums.Constant import Constant
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(Constant.DEFAULT_ENCODING), salt)
25
- return hashed.decode(Constant.DEFAULT_ENCODING)
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(Constant.DEFAULT_ENCODING), hashed_password.encode(Constant.DEFAULT_ENCODING)
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(Constant.DEFAULT_ENCODING))
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,9 @@
1
+ from enum import StrEnum
2
+
3
+ class FileType(StrEnum):
4
+ IMAGE = "image"
5
+ VIDEO = "video"
6
+ AUDIO = "audio"
7
+ DOCUMENT = "document"
8
+ ARCHIVE = "archive"
9
+ OTHER = "other"
@@ -1,5 +1,5 @@
1
1
  from enum import StrEnum
2
2
 
3
3
  class GraphqlOperation(StrEnum):
4
- QUERY = 'query'
5
- MUTATION = 'mutation'
4
+ QUERY = "query"
5
+ MUTATION = "mutation"
@@ -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)